1" Vim functions for file type detection 2" 3" Maintainer: Bram Moolenaar <[email protected]> 4" Last Change: 2020 Aug 17 5 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 13func dist#ft#Check_inp() 14 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. 35func dist#ft#FTasm() 36 " make sure b:asmsyntax exists 37 if !exists("b:asmsyntax") 38 let b:asmsyntax = "" 39 endif 40 41 if b:asmsyntax == "" 42 call dist#ft#FTasmsyntax() 43 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 57func dist#ft#FTasmsyntax() 58 " see if file contains any asmsyntax=foo overrides. If so, change 59 " b:asmsyntax appropriately 60 let head = " ".getline(1)." ".getline(2)." ".getline(3)." ".getline(4). 61 \" ".getline(5)." " 62 let match = matchstr(head, '\sasmsyntax=\zs[a-zA-Z0-9]\+\ze\s') 63 if match != '' 64 let b:asmsyntax = match 65 elseif ((head =~? '\.title') || (head =~? '\.ident') || (head =~? '\.macro') || (head =~? '\.subtitle') || (head =~? '\.library')) 66 let b:asmsyntax = "vmasm" 67 endif 68endfunc 69 70" Check if one of the first five lines contains "VB_Name". In that case it is 71" probably a Visual Basic file. Otherwise it's assumed to be "alt" filetype. 72func dist#ft#FTVB(alt) 73 if getline(1).getline(2).getline(3).getline(4).getline(5) =~? 'VB_Name\|Begin VB\.\(Form\|MDIForm\|UserControl\)' 74 setf vb 75 else 76 exe "setf " . a:alt 77 endif 78endfunc 79 80func dist#ft#FTbtm() 81 if exists("g:dosbatch_syntax_for_btm") && g:dosbatch_syntax_for_btm 82 setf dosbatch 83 else 84 setf btm 85 endif 86endfunc 87 88func dist#ft#BindzoneCheck(default) 89 if getline(1).getline(2).getline(3).getline(4) =~ '^; <<>> DiG [0-9.]\+.* <<>>\|$ORIGIN\|$TTL\|IN\s\+SOA' 90 setf bindzone 91 elseif a:default != '' 92 exe 'setf ' . a:default 93 endif 94endfunc 95 96func dist#ft#FTlpc() 97 if exists("g:lpc_syntax_for_c") 98 let lnum = 1 99 while lnum <= 12 100 if getline(lnum) =~# '^\(//\|inherit\|private\|protected\|nosave\|string\|object\|mapping\|mixed\)' 101 setf lpc 102 return 103 endif 104 let lnum = lnum + 1 105 endwhile 106 endif 107 setf c 108endfunc 109 110func dist#ft#FTheader() 111 if match(getline(1, min([line("$"), 200])), '^@\(interface\|end\|class\)') > -1 112 if exists("g:c_syntax_for_h") 113 setf objc 114 else 115 setf objcpp 116 endif 117 elseif exists("g:c_syntax_for_h") 118 setf c 119 elseif exists("g:ch_syntax_for_h") 120 setf ch 121 else 122 setf cpp 123 endif 124endfunc 125 126" This function checks if one of the first ten lines start with a '@'. In 127" that case it is probably a change file. 128" If the first line starts with # or ! it's probably a ch file. 129" If a line has "main", "include", "//" or "/*" it's probably ch. 130" Otherwise CHILL is assumed. 131func dist#ft#FTchange() 132 let lnum = 1 133 while lnum <= 10 134 if getline(lnum)[0] == '@' 135 setf change 136 return 137 endif 138 if lnum == 1 && (getline(1)[0] == '#' || getline(1)[0] == '!') 139 setf ch 140 return 141 endif 142 if getline(lnum) =~ "MODULE" 143 setf chill 144 return 145 endif 146 if getline(lnum) =~ 'main\s*(\|#\s*include\|//' 147 setf ch 148 return 149 endif 150 let lnum = lnum + 1 151 endwhile 152 setf chill 153endfunc 154 155func dist#ft#FTent() 156 " This function checks for valid cl syntax in the first five lines. 157 " Look for either an opening comment, '#', or a block start, '{". 158 " If not found, assume SGML. 159 let lnum = 1 160 while lnum < 6 161 let line = getline(lnum) 162 if line =~ '^\s*[#{]' 163 setf cl 164 return 165 elseif line !~ '^\s*$' 166 " Not a blank line, not a comment, and not a block start, 167 " so doesn't look like valid cl code. 168 break 169 endif 170 let lnum = lnum + 1 171 endw 172 setf dtd 173endfunc 174 175func dist#ft#EuphoriaCheck() 176 if exists('g:filetype_euphoria') 177 exe 'setf ' . g:filetype_euphoria 178 else 179 setf euphoria3 180 endif 181endfunc 182 183func dist#ft#DtraceCheck() 184 let lines = getline(1, min([line("$"), 100])) 185 if match(lines, '^module\>\|^import\>') > -1 186 " D files often start with a module and/or import statement. 187 setf d 188 elseif match(lines, '^#!\S\+dtrace\|#pragma\s\+D\s\+option\|:\S\{-}:\S\{-}:') > -1 189 setf dtrace 190 else 191 setf d 192 endif 193endfunc 194 195func dist#ft#FTe() 196 if exists('g:filetype_euphoria') 197 exe 'setf ' . g:filetype_euphoria 198 else 199 let n = 1 200 while n < 100 && n <= line("$") 201 if getline(n) =~ "^\\s*\\(<'\\|'>\\)\\s*$" 202 setf specman 203 return 204 endif 205 let n = n + 1 206 endwhile 207 setf eiffel 208 endif 209endfunc 210 211" Distinguish between HTML, XHTML and Django 212func dist#ft#FThtml() 213 let n = 1 214 while n < 10 && n <= line("$") 215 if getline(n) =~ '\<DTD\s\+XHTML\s' 216 setf xhtml 217 return 218 endif 219 if getline(n) =~ '{%\s*\(extends\|block\|load\)\>\|{#\s\+' 220 setf htmldjango 221 return 222 endif 223 let n = n + 1 224 endwhile 225 setf FALLBACK html 226endfunc 227 228" Distinguish between standard IDL and MS-IDL 229func dist#ft#FTidl() 230 let n = 1 231 while n < 50 && n <= line("$") 232 if getline(n) =~ '^\s*import\s\+"\(unknwn\|objidl\)\.idl"' 233 setf msidl 234 return 235 endif 236 let n = n + 1 237 endwhile 238 setf idl 239endfunc 240 241" Distinguish between "default" and Cproto prototype file. */ 242func dist#ft#ProtoCheck(default) 243 " Cproto files have a comment in the first line and a function prototype in 244 " the second line, it always ends in ";". Indent files may also have 245 " comments, thus we can't match comments to see the difference. 246 " IDL files can have a single ';' in the second line, require at least one 247 " chacter before the ';'. 248 if getline(2) =~ '.;$' 249 setf cpp 250 else 251 exe 'setf ' . a:default 252 endif 253endfunc 254 255func dist#ft#FTm() 256 let n = 1 257 let saw_comment = 0 " Whether we've seen a multiline comment leader. 258 while n < 100 259 let line = getline(n) 260 if line =~ '^\s*/\*' 261 " /* ... */ is a comment in Objective C and Murphi, so we can't conclude 262 " it's either of them yet, but track this as a hint in case we don't see 263 " anything more definitive. 264 let saw_comment = 1 265 endif 266 if line =~ '^\s*\(#\s*\(include\|import\)\>\|@import\>\|//\)' 267 setf objc 268 return 269 endif 270 if line =~ '^\s*%' 271 setf matlab 272 return 273 endif 274 if line =~ '^\s*(\*' 275 setf mma 276 return 277 endif 278 if line =~ '^\c\s*\(\(type\|var\)\>\|--\)' 279 setf murphi 280 return 281 endif 282 let n = n + 1 283 endwhile 284 285 if saw_comment 286 " We didn't see anything definitive, but this looks like either Objective C 287 " or Murphi based on the comment leader. Assume the former as it is more 288 " common. 289 setf objc 290 elseif exists("g:filetype_m") 291 " Use user specified default filetype for .m 292 exe "setf " . g:filetype_m 293 else 294 " Default is matlab 295 setf matlab 296 endif 297endfunc 298 299func dist#ft#FTmms() 300 let n = 1 301 while n < 20 302 let line = getline(n) 303 if line =~ '^\s*\(%\|//\)' || line =~ '^\*' 304 setf mmix 305 return 306 endif 307 if line =~ '^\s*#' 308 setf make 309 return 310 endif 311 let n = n + 1 312 endwhile 313 setf mmix 314endfunc 315 316" This function checks if one of the first five lines start with a dot. In 317" that case it is probably an nroff file: 'filetype' is set and 1 is returned. 318func dist#ft#FTnroff() 319 if getline(1)[0] . getline(2)[0] . getline(3)[0] . getline(4)[0] . getline(5)[0] =~ '\.' 320 setf nroff 321 return 1 322 endif 323 return 0 324endfunc 325 326func dist#ft#FTmm() 327 let n = 1 328 while n < 20 329 let line = getline(n) 330 if line =~ '^\s*\(#\s*\(include\|import\)\>\|@import\>\|/\*\)' 331 setf objcpp 332 return 333 endif 334 let n = n + 1 335 endwhile 336 setf nroff 337endfunc 338 339func dist#ft#FTpl() 340 if exists("g:filetype_pl") 341 exe "setf " . g:filetype_pl 342 else 343 " recognize Prolog by specific text in the first non-empty line 344 " require a blank after the '%' because Perl uses "%list" and "%translate" 345 let l = getline(nextnonblank(1)) 346 if l =~ '\<prolog\>' || l =~ '^\s*\(%\+\(\s\|$\)\|/\*\)' || l =~ ':-' 347 setf prolog 348 else 349 setf perl 350 endif 351 endif 352endfunc 353 354func dist#ft#FTinc() 355 if exists("g:filetype_inc") 356 exe "setf " . g:filetype_inc 357 else 358 let lines = getline(1).getline(2).getline(3) 359 if lines =~? "perlscript" 360 setf aspperl 361 elseif lines =~ "<%" 362 setf aspvbs 363 elseif lines =~ "<?" 364 setf php 365 " Pascal supports // comments but they're vary rarely used for file 366 " headers so assume POV-Ray 367 elseif lines =~ '^\s*\%({\|(\*\)' || lines =~? s:ft_pascal_keywords 368 setf pascal 369 else 370 call dist#ft#FTasmsyntax() 371 if exists("b:asmsyntax") 372 exe "setf " . fnameescape(b:asmsyntax) 373 else 374 setf pov 375 endif 376 endif 377 endif 378endfunc 379 380func dist#ft#FTprogress_cweb() 381 if exists("g:filetype_w") 382 exe "setf " . g:filetype_w 383 return 384 endif 385 if getline(1) =~ '&ANALYZE' || getline(3) =~ '&GLOBAL-DEFINE' 386 setf progress 387 else 388 setf cweb 389 endif 390endfunc 391 392func dist#ft#FTprogress_asm() 393 if exists("g:filetype_i") 394 exe "setf " . g:filetype_i 395 return 396 endif 397 " This function checks for an assembly comment the first ten lines. 398 " If not found, assume Progress. 399 let lnum = 1 400 while lnum <= 10 && lnum < line('$') 401 let line = getline(lnum) 402 if line =~ '^\s*;' || line =~ '^\*' 403 call dist#ft#FTasm() 404 return 405 elseif line !~ '^\s*$' || line =~ '^/\*' 406 " Not an empty line: Doesn't look like valid assembly code. 407 " Or it looks like a Progress /* comment 408 break 409 endif 410 let lnum = lnum + 1 411 endw 412 setf progress 413endfunc 414 415let s:ft_pascal_comments = '^\s*\%({\|(\*\|//\)' 416let s:ft_pascal_keywords = '^\s*\%(program\|unit\|library\|uses\|begin\|procedure\|function\|const\|type\|var\)\>' 417 418func dist#ft#FTprogress_pascal() 419 if exists("g:filetype_p") 420 exe "setf " . g:filetype_p 421 return 422 endif 423 " This function checks for valid Pascal syntax in the first ten lines. 424 " Look for either an opening comment or a program start. 425 " If not found, assume Progress. 426 let lnum = 1 427 while lnum <= 10 && lnum < line('$') 428 let line = getline(lnum) 429 if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords 430 setf pascal 431 return 432 elseif line !~ '^\s*$' || line =~ '^/\*' 433 " Not an empty line: Doesn't look like valid Pascal code. 434 " Or it looks like a Progress /* comment 435 break 436 endif 437 let lnum = lnum + 1 438 endw 439 setf progress 440endfunc 441 442func dist#ft#FTpp() 443 if exists("g:filetype_pp") 444 exe "setf " . g:filetype_pp 445 else 446 let line = getline(nextnonblank(1)) 447 if line =~ s:ft_pascal_comments || line =~? s:ft_pascal_keywords 448 setf pascal 449 else 450 setf puppet 451 endif 452 endif 453endfunc 454 455func dist#ft#FTr() 456 let max = line("$") > 50 ? 50 : line("$") 457 458 for n in range(1, max) 459 " Rebol is easy to recognize, check for that first 460 if getline(n) =~? '\<REBOL\>' 461 setf rebol 462 return 463 endif 464 endfor 465 466 for n in range(1, max) 467 " R has # comments 468 if getline(n) =~ '^\s*#' 469 setf r 470 return 471 endif 472 " Rexx has /* comments */ 473 if getline(n) =~ '^\s*/\*' 474 setf rexx 475 return 476 endif 477 endfor 478 479 " Nothing recognized, use user default or assume Rexx 480 if exists("g:filetype_r") 481 exe "setf " . g:filetype_r 482 else 483 " Rexx used to be the default, but R appears to be much more popular. 484 setf r 485 endif 486endfunc 487 488func dist#ft#McSetf() 489 " Rely on the file to start with a comment. 490 " MS message text files use ';', Sendmail files use '#' or 'dnl' 491 for lnum in range(1, min([line("$"), 20])) 492 let line = getline(lnum) 493 if line =~ '^\s*\(#\|dnl\)' 494 setf m4 " Sendmail .mc file 495 return 496 elseif line =~ '^\s*;' 497 setf msmessages " MS Message text file 498 return 499 endif 500 endfor 501 setf m4 " Default: Sendmail .mc file 502endfunc 503 504" Called from filetype.vim and scripts.vim. 505func dist#ft#SetFileTypeSH(name) 506 if did_filetype() 507 " Filetype was already detected 508 return 509 endif 510 if expand("<amatch>") =~ g:ft_ignore_pat 511 return 512 endif 513 if a:name =~ '\<csh\>' 514 " Some .sh scripts contain #!/bin/csh. 515 call dist#ft#SetFileTypeShell("csh") 516 return 517 elseif a:name =~ '\<tcsh\>' 518 " Some .sh scripts contain #!/bin/tcsh. 519 call dist#ft#SetFileTypeShell("tcsh") 520 return 521 elseif a:name =~ '\<zsh\>' 522 " Some .sh scripts contain #!/bin/zsh. 523 call dist#ft#SetFileTypeShell("zsh") 524 return 525 elseif a:name =~ '\<ksh\>' 526 let b:is_kornshell = 1 527 if exists("b:is_bash") 528 unlet b:is_bash 529 endif 530 if exists("b:is_sh") 531 unlet b:is_sh 532 endif 533 elseif exists("g:bash_is_sh") || a:name =~ '\<bash\>' || a:name =~ '\<bash2\>' 534 let b:is_bash = 1 535 if exists("b:is_kornshell") 536 unlet b:is_kornshell 537 endif 538 if exists("b:is_sh") 539 unlet b:is_sh 540 endif 541 elseif a:name =~ '\<sh\>' 542 let b:is_sh = 1 543 if exists("b:is_kornshell") 544 unlet b:is_kornshell 545 endif 546 if exists("b:is_bash") 547 unlet b:is_bash 548 endif 549 endif 550 call dist#ft#SetFileTypeShell("sh") 551endfunc 552 553" For shell-like file types, check for an "exec" command hidden in a comment, 554" as used for Tcl. 555" Also called from scripts.vim, thus can't be local to this script. 556func dist#ft#SetFileTypeShell(name) 557 if did_filetype() 558 " Filetype was already detected 559 return 560 endif 561 if expand("<amatch>") =~ g:ft_ignore_pat 562 return 563 endif 564 let l = 2 565 while l < 20 && l < line("$") && getline(l) =~ '^\s*\(#\|$\)' 566 " Skip empty and comment lines. 567 let l = l + 1 568 endwhile 569 if l < line("$") && getline(l) =~ '\s*exec\s' && getline(l - 1) =~ '^\s*#.*\\$' 570 " Found an "exec" line after a comment with continuation 571 let n = substitute(getline(l),'\s*exec\s\+\([^ ]*/\)\=', '', '') 572 if n =~ '\<tclsh\|\<wish' 573 setf tcl 574 return 575 endif 576 endif 577 exe "setf " . a:name 578endfunc 579 580func dist#ft#CSH() 581 if did_filetype() 582 " Filetype was already detected 583 return 584 endif 585 if exists("g:filetype_csh") 586 call dist#ft#SetFileTypeShell(g:filetype_csh) 587 elseif &shell =~ "tcsh" 588 call dist#ft#SetFileTypeShell("tcsh") 589 else 590 call dist#ft#SetFileTypeShell("csh") 591 endif 592endfunc 593 594let s:ft_rules_udev_rules_pattern = '^\s*\cudev_rules\s*=\s*"\([^"]\{-1,}\)/*".*' 595func dist#ft#FTRules() 596 let path = expand('<amatch>:p') 597 if path =~ '/\(etc/udev/\%(rules\.d/\)\=.*\.rules\|\%(usr/\)\=lib/udev/\%(rules\.d/\)\=.*\.rules\)$' 598 setf udevrules 599 return 600 endif 601 if path =~ '^/etc/ufw/' 602 setf conf " Better than hog 603 return 604 endif 605 if path =~ '^/\(etc\|usr/share\)/polkit-1/rules\.d' 606 setf javascript 607 return 608 endif 609 try 610 let config_lines = readfile('/etc/udev/udev.conf') 611 catch /^Vim\%((\a\+)\)\=:E484/ 612 setf hog 613 return 614 endtry 615 let dir = expand('<amatch>:p:h') 616 for line in config_lines 617 if line =~ s:ft_rules_udev_rules_pattern 618 let udev_rules = substitute(line, s:ft_rules_udev_rules_pattern, '\1', "") 619 if dir == udev_rules 620 setf udevrules 621 endif 622 break 623 endif 624 endfor 625 setf hog 626endfunc 627 628func dist#ft#SQL() 629 if exists("g:filetype_sql") 630 exe "setf " . g:filetype_sql 631 else 632 setf sql 633 endif 634endfunc 635 636" If the file has an extension of 't' and is in a directory 't' or 'xt' then 637" it is almost certainly a Perl test file. 638" If the first line starts with '#' and contains 'perl' it's probably a Perl 639" file. 640" (Slow test) If a file contains a 'use' statement then it is almost certainly 641" a Perl file. 642func dist#ft#FTperl() 643 let dirname = expand("%:p:h:t") 644 if expand("%:e") == 't' && (dirname == 't' || dirname == 'xt') 645 setf perl 646 return 1 647 endif 648 if getline(1)[0] == '#' && getline(1) =~ 'perl' 649 setf perl 650 return 1 651 endif 652 let save_cursor = getpos('.') 653 call cursor(1,1) 654 let has_use = search('^use\s\s*\k', 'c', 30) 655 call setpos('.', save_cursor) 656 if has_use 657 setf perl 658 return 1 659 endif 660 return 0 661endfunc 662 663" Choose context, plaintex, or tex (LaTeX) based on these rules: 664" 1. Check the first line of the file for "%&<format>". 665" 2. Check the first 1000 non-comment lines for LaTeX or ConTeXt keywords. 666" 3. Default to "plain" or to g:tex_flavor, can be set in user's vimrc. 667func dist#ft#FTtex() 668 let firstline = getline(1) 669 if firstline =~ '^%&\s*\a\+' 670 let format = tolower(matchstr(firstline, '\a\+')) 671 let format = substitute(format, 'pdf', '', '') 672 if format == 'tex' 673 let format = 'latex' 674 elseif format == 'plaintex' 675 let format = 'plain' 676 endif 677 elseif expand('%') =~ 'tex/context/.*/.*.tex' 678 let format = 'context' 679 else 680 " Default value, may be changed later: 681 let format = exists("g:tex_flavor") ? g:tex_flavor : 'plain' 682 " Save position, go to the top of the file, find first non-comment line. 683 let save_cursor = getpos('.') 684 call cursor(1,1) 685 let firstNC = search('^\s*[^[:space:]%]', 'c', 1000) 686 if firstNC " Check the next thousand lines for a LaTeX or ConTeXt keyword. 687 let lpat = 'documentclass\>\|usepackage\>\|begin{\|newcommand\>\|renewcommand\>' 688 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\>' 689 let kwline = search('^\s*\\\%(' . lpat . '\)\|^\s*\\\(' . cpat . '\)', 690 \ 'cnp', firstNC + 1000) 691 if kwline == 1 " lpat matched 692 let format = 'latex' 693 elseif kwline == 2 " cpat matched 694 let format = 'context' 695 endif " If neither matched, keep default set above. 696 " let lline = search('^\s*\\\%(' . lpat . '\)', 'cn', firstNC + 1000) 697 " let cline = search('^\s*\\\%(' . cpat . '\)', 'cn', firstNC + 1000) 698 " if cline > 0 699 " let format = 'context' 700 " endif 701 " if lline > 0 && (cline == 0 || cline > lline) 702 " let format = 'tex' 703 " endif 704 endif " firstNC 705 call setpos('.', save_cursor) 706 endif " firstline =~ '^%&\s*\a\+' 707 708 " Translation from formats to file types. TODO: add AMSTeX, RevTex, others? 709 if format == 'plain' 710 setf plaintex 711 elseif format == 'context' 712 setf context 713 else " probably LaTeX 714 setf tex 715 endif 716 return 717endfunc 718 719func dist#ft#FTxml() 720 let n = 1 721 while n < 100 && n <= line("$") 722 let line = getline(n) 723 " DocBook 4 or DocBook 5. 724 let is_docbook4 = line =~ '<!DOCTYPE.*DocBook' 725 let is_docbook5 = line =~ ' xmlns="http://docbook.org/ns/docbook"' 726 if is_docbook4 || is_docbook5 727 let b:docbk_type = "xml" 728 if is_docbook5 729 let b:docbk_ver = 5 730 else 731 let b:docbk_ver = 4 732 endif 733 setf docbk 734 return 735 endif 736 if line =~ 'xmlns:xbl="http://www.mozilla.org/xbl"' 737 setf xbl 738 return 739 endif 740 let n += 1 741 endwhile 742 setf xml 743endfunc 744 745func dist#ft#FTy() 746 let n = 1 747 while n < 100 && n <= line("$") 748 let line = getline(n) 749 if line =~ '^\s*%' 750 setf yacc 751 return 752 endif 753 if getline(n) =~ '^\s*\(#\|class\>\)' && getline(n) !~ '^\s*#\s*include' 754 setf racc 755 return 756 endif 757 let n = n + 1 758 endwhile 759 setf yacc 760endfunc 761 762func dist#ft#Redif() 763 let lnum = 1 764 while lnum <= 5 && lnum < line('$') 765 if getline(lnum) =~ "^\ctemplate-type:" 766 setf redif 767 return 768 endif 769 let lnum = lnum + 1 770 endwhile 771endfunc 772 773 774" Restore 'cpoptions' 775let &cpo = s:cpo_save 776unlet s:cpo_save 777