Vim Tips Wiki
Line 327: Line 327:
 
==Adding syntax to Cream EnhancedCommentify==
 
==Adding syntax to Cream EnhancedCommentify==
   
If you have [[http://cream.sourceforge.net/ Cream]], you
+
If you have [http://cream.sourceforge.net/ Cream], you
 
might have noticed that you get an interesting error when
 
might have noticed that you get an interesting error when
 
you try to highlight your newly created syntax. Never fear there
 
you try to highlight your newly created syntax. Never fear there

Revision as of 05:23, 30 April 2008

Proposed tip Please edit this page to improve it, or add your comments below (do not use the discussion page).

Please use new tips to discuss whether this page should be a permanent tip, or whether it should be merged to an existing tip.
created April 29, 2008 · complexity basic · author Clearmoments · version 7.0

Introduction

Since there isn't a lot of material on creating your own syntax files in Vim, I've decided to write a tutorial on the process. Currently, this tutorial only shows how to make Vim interpret the syntax of files by their file extension. Another tutorial may extend this to show how to interpret the syntax of files by their contents.

Example: Celestia star catalogs

For this tutorial I am creating a syntax file for Celestia star catalogs. Celestia is a great program for anyone who likes astronomy and space, but I digress. All we need to know for this tutorial is that a star catalog lists a star name along with its positional information, distance and attributes (color, radius, mass, brightness). An example entry in a star catalog file (.stc) can be:

600000 "My Star"
{
  RA 24.406489
  Dec -9.404052
  SpectralType "Q"
  Mass 1.09
  AbsMag 1.29
  Distance 124.729260
}

As you can see it consists of a number, a string, and a block ({...}), with some keywords within that block ("RA", "Dec" and "Distance"). Comments are marked out by a "#" like in shell scripts or conf files. In fact, the syntax looks a lot like a conf file. There can be multiple entries like this with a number (the HIP number), the string, and the block containing the attributes. Celestia gets more complicated than this because you can have multiple stars going around a barycenter, etc, but we are not going to get fancy. We'll cover only stars.

Syntax files

Get your directory straight

Syntax files are editor scripts, just like everything else, and are located in your personal $HOME/.vim/syntax directory. If that directory does not exist you can create it. There should also be a system-wide syntax file directory. You can see these in Vim with the command:

:set rtp?

The system wide syntax directories are OS and system dependent.

Unix

/usr/share/vim/vimxx/syntax

Where xx is the vim version. You can verify its location using the

previous editor command.

Windows

X:\%PROGRAMFILES%\vim\vim71\syntax
[cream users] X:\%PROGRAMFILES%\vim\vim71\cream\syntax

Under Windows you must create a %HOME% environment variable manually. As per the Change_the_color_scheme tip:


On Windows, the $HOME environment variable can be set by going to your desktop, right click on "My Computer", click the "Advanced" tab, select "Environment Variables". If HOME is not in the list of variables, create a new variable named HOME and point it to the location of your vimrc.

Mac OS

I am going to assume that Mac OS is the same as the Unix. See Unix.

The system-wide syntax directory is going to be one of the entries separated by commas (and frankly I'm not expert enough to know which one it is). The first value I know is your Vim personal syntax directory. You may want to study those syntax files and see how they are put together before playing around with this one its your choice.

Build a syntax file

First, create a new file in Vim, and add the following contents:

" Vim syntax file
" Language: Celestia Star Catalogs
" Maintainer: Kevin Lauder
" Latest Revision: 26 April 2008

if exists("b:current_syntax")
  finish
endif

Vim comments start with a quote. So I am following the convention of the built-in syntax files, and making a little comment flower box. The test if exists("b:current_syntax") ... checks whether an earlier file has defined a syntax already. If so, the script exits with finish.

Keyword, match & region elements

There are three major syntax elements, and commands to describe those elements. In order to syntax highlight, we must be able to describe what to highlight. Here is an example of what they look like:

" Keywords
syn keyword syntaxElementKeyword keyword1 keyword2 nextgroup=syntaxElement2

" Matches
syn match syntaxElementMatch 'regexp' contains=syntaxElement1 nextgroup=syntaxElement2 skipwhite

" Regions
syn region syntaxElementRegion start='x' end='y'

Keywords

Keywords are simple. Take for example the programming language BASIC. In BASIC there are several keywords like PRINT, OPEN and IF. Let's say you would like the editor to recognize them. You can define them like so:

syn keyword basicLanguageKeywords PRINT OPEN IF

For now we are not going to worry about nextgroup=.

The editor will now recognize the keywords PRINT, OPEN and IF as syntax elements of type basicLanguageKeywords. You can add more on the same line, or add another line with the same type (basicLanguageKeywords). In other words if I wanted to add the keywords DO, WHILE and WEND to my list, I could add to the previous line like this:

syn keyword basicLanguageKeywords PRINT OPEN IF DO WHILE WEND

Or, I could add another line, list this:

syn keyword basicLanguageKeywords PRINT OPEN IF
syn keyword basicLanguageKeywords DO WHILE WEND

Let's use a more relevant example. Take our star catalog entry from above

600000 "My Star"
{
  RA 24.406489
  Dec -9.404052
  SpectralType "Q"
  Mass 1.09
  AbsMag 1.29
  Distance 124.729260
}

We can group the following keywords as part of a syntax element called celstcStarBlockCmd by adding the following to our syntax file.

syn keyword celstcStarBlockCmd RA Dec SpectralType Mass Distance AbsMag

The editor will now recognize them. Maybe that's enough for your purposes, but I wanted to make things a little more interesting with matches following the keywords, like those numbers and string values. How do we get those to be recognized?

Matches (and addendum to keywords)

All this keyword stuff logically leads to matches. Take the above example once again. After the keywords ("RA", "Dec", "AbsMag" etc) there are numbers. Let's say we want Vim to know that following a certain keyword there is going to be some set of characters to follow (defined as a regular expression maybe).

This is where matches come in; along with an additional caveat to using keywords, the nextgroup and skipwhite arguments as seen above.

syn match celstcNumber '\d\+'
syn keyword celstcStarBlockCmd RA Dec Mass Distance AbsMag nextgroup=celstcNumber skipwhite

Now as you can see the match was given a regular expression \d\+ meaning to match one or more (\+) digits 0-9 (\d). The keyword syntax element celstcStarBlockCmd has been modified slightly because following the SpectralType keyword is not a number but a string. We will address that problem shortly by creating another regular expression to match that. For now, notice the nextgroup argument. We are telling the editor to expect a celstcNumber after the keyword. So that's the first pattern the editor will attempt to match after finding one of those keywords.

The skipwhite argument simply tells the editor to expect some whitespace (tabs, spaces etc) between the keyword and the number.

You may have noticed a slight problem with my implementation. It will only match numbers like 19938, 93, and 0. It won't match decimals 3.91881 or negative numbers (-9).

How do we fix that? With more interesting regular expressions of course! This was taken from one of the existing Vim syntax files and modified slightly to work for our needs.

" Regular int like number with - + or nothing in front
syn match celstcNumber '\d\+'
syn match celstcNumber '[-+]\d\+'

" Floating point number with decimal no E or e (+,-)
syn match celstcNumber '\d\+\.\d*'
syn match celstcNumber '[-+]\d\+\.\d*'

" Floating point like number with E and no decimal point (+,-)
syn match celstcNumber '[-+]\=\d[[:digit:]]*[eE][\-+]\=\d\+'
syn match celstcNumber '\d[[:digit:]]*[eE][\-+]\=\d\+'

" Floating point like number with E and decimal point (+,-)
syn match celstcNumber '[-+]\=\d[[:digit:]]*\.\d*[eE][\-+]\=\d\+'
syn match celstcNumber '\d[[:digit:]]*\.\d*[eE][\-+]\=\d\+'

Notice how we can keep creating more lines of syn match celstcNumber 'a pattern' to match all those patterns as one syntax element type (in this case celstcNumber.)

Regions

But we have another challenge that waits us. Let's look at the star catalog entry again.

600000 "My Star"
{
  RA 24.406489
  Dec -9.404052
  SpectralType "Q"
  Mass 1.09
  AbsMag 1.29
  Distance 124.729260
}

The first number is outside of the brackets, and that's not really a number, that's an HIP catalog entry (more like an ID number rather than a value with physical meaning like mass or distance). The real numbers are the arguments to the keywords (like "RA" and "Dec".) Since the definition of a celstcNumber conflicts with the pattern of an HIP catalog number since they both consist of the digits 0-9. How do we fix that discrepancy?

Note that numbers with values exist only within brackets. Outside of the brackets it is an ID number rather than a value. We have to add another argument to the keyword and match definitions, and introduce another type of syntax element: a region.

First, we have to let the editor know that the aforementioned keywords only exist within brackets. Second, we specify that celstcNumbers only exist within brackets. This is the concept of a region.

syn region celstcDescBlock start="{" end="}" fold transparent

There we go, and we threw in some arguments for kicks. The fold argument means that Vim can increase the fold count inside brackets so you can press Ctrl+<F9> to expand and contract the code. The transparent is the important keyword here. It tells the editor to continue to apply matches and keywords to what's inside the region. Otherwise the region would not be colorized properly.

We must add another additional argument to finish off everything.

syn region celstcDescBlock start="{" end="}" fold transparent contains=celstcNumber,celstcStarBlockCmd

The contains argument tells the editor which syntax elements this region will contain. In this case keywords and numbers. But we have strings too, right? So let's implement the required syntax elements since we know all about keywords, matches and regions now. In addition we pickup another argument along the way, contained.

Let's define comments as a syntax element and see how contained works.

syn keyword celstcTodo      contained TODO FIXME XXX NOTE
syn match   celstcComment   "#.*$" contains=celstcTodo

Comments start with a "#" and run until the end of line. So thats a simple regular expression '#.*$'. Starts with a "#" and match all characters until the end of a line.

contained simply tells the editor that the keyword is only valid when contained by another syntax element, in this case a celstcTodo is only treated as a separate syntax element when contained by celstcComment.

So let's redefine everything from before, and implement all the required grammar, and bring this puppy together.

Bringing it together

syn keyword celstcTodo      contained TODO FIXME XXX NOTE
syn match   celstcComment   "#.*$" contains=celstcTodo

"----------------------------------------------------------------
" Celestia Star Catalog Numbers
"----------------------------------------------------------------

" Regular int like number with - + or nothing in front
syn match   celstcNumber    '\d\+' contained display
syn match   celstcNumber    '[-+]\d\+' contained display

" Floating point number with decimal no E or e (+,-)
syn match   celstcNumber    '\d\+\.\d*' contained display
syn match   celstcNumber    '[-+]\d\+\.\d*' contained display

" Floating point like number with E and no decimal point (+,-)
syn match   celstcNumber    '[-+]\=\d[[:digit:]]*[eE][\-+]\=\d\+' contained display
syn match   celstcNumber    '\d[[:digit:]]*[eE][\-+]\=\d\+' contained display

" Floating point like number with E and decimal point (+,-)
syn match   celstcNumber    '[-+]\=\d[[:digit:]]*\.\d*[eE][\-+]\=\d\+' contained display
syn match   celstcNumber    '\d[[:digit:]]*\.\d*[eE][\-+]\=\d\+' contained display

syn region  celstcString        start='"' end='"' contained
syn region  celstcDescString    start='"' end='"'

syn match   celstcHip     '\d\{1,6}'      nextgroup=celstcString
syn region  celstcDescBlock     start="{" end="}" fold transparent      contains=ALLBUT,celstcHip,celstcDescString

syn keyword celstcStarBlockCmd  RA Dec Distance AbsMag nextgroup=celstcNumber
syn keyword celstcStarBlockCmd  SpectralType nextgroup=celstcString

Telling Vim how to highlight + final touches

You've reached the easiest part, just take the syntax element names you've used and use the hi def link command to tell Vim how to highlight.

Set the b:current_syntax variable to a name. I called it "celstc". You'll need that name to modify the Un/Commentify (<F6>/Shift+<F6>) script in Cream for example to block comment-out lines with your new file types.

let b:current_syntax = "celstc"

hi def link celstcTodo              Todo
hi def link celstcComment           Comment
hi def link celstcStarBlockCmd      Statement
hi def link celstcHip               Type
hi def link celstcString            Constant
hi def link celstcDescString        PreProc
hi def link celstcNumber            Constant

The hi def link command has different types of highlighting options that I wont go into. But the ones I'm using here are

  • Todo: used for the todo comments (ones that have "TODO: blah blah" in them)
  • Comment: indicates a code comment
  • Statement: a code statement like a for loop or something
  • Type: a user defined type generally
  • PreProc: a pre-processor (like a C #include <stdio.h>)
  • Constant: like a string or number in code

These of course are guidelines. For our purposes we really don't have statements or pre-processor commands, since this is really more like a conf file or like HTML. As you can see I set the celstcStarBlockCmds to use the Statement highlighting. As for the celstcHip I set that to a Type (seems like a close match to meaning.)

You can view more options in the vim help

   :help syntax

And look at the syntax file help


Save your work

Finally save your file as celstc.vim (that's what I called it), in your Vim syntax directory (preferably your local one $HOME/.vim/syntax).

Setup your filetype.vim file

Okay, now we have to make sure Vim knows how to interpret your file. First if $HOME/.vim/filetype.vim exists then you are going to find the part of the file that looks like this:

augroup filetypedetect
au! BufRead,BufNewFile *.stc setfiletype celstc
augroup END


Adding syntax to Cream EnhancedCommentify

If you have Cream, you might have noticed that you get an interesting error when you try to highlight your newly created syntax. Never fear there is a fix for that too.

Locate EnhancedCommentify.vim for your installation

First you have to find your EnhancedCommentify.vim file in your Cream installation.

Windows

Under windows you can find your EnhancedCommentify.vim file in the X:\%PROGRAMFILES%\vim\vimxx\cream directory. Where xx is the Vim version number


Unix & Mac OS

You will find it generally in /usr/share/vim/cream/ and go EnhancedCommentify.vim. Make a backup copy of this file before continuing!

Edit the GetFileTypeSettings function

Open the file up in Vim and locate GetFileTypeSettings function in the commentify file. You're going to see sections like this:

Code Snippet 1
"
" GetFileTypeSettings(ft)
"	ft	    -- filetype
"
" This functions sets some buffer-variables, which control the comment
" strings and 'empty lines'-handling.
"
function s:GetFileTypeSettings(ft)
    let fileType = a:ft

    " I learned about the commentstring option. Let's use it.
    " For now we ignore it, if it is "/*%s*/". This is the
    " default. We cannot check wether this is default or C or
    " something other like CSS, etc. We have to wait, until the
    " filetypes adopt this option.
    if &commentstring != "/*%s*/" && !b:ECuseSyntax
	let b:ECcommentOpen =
		    \ substitute(&commentstring, '%s.*', "", "")
	let b:ECcommentClose =
		    \ substitute(&commentstring, '.*%s', "", "")
    " Multipart comments:
    elseif fileType =~ '^\(c\|b\|css\|csc\|cupl\|indent\|jam\|lex\|lifelines\|'.
		\ 'lite\|nqc\|phtml\|progress\|rexx\|rpl\|sas\|sdl\|sl\|'.
		\ 'strace\|xpm\|yacc\)$'


You will see sections like this in the file.

Code Snippet 2
    elseif fileType == 'atlas'
	let b:ECcommentOpen = 'C'
	let b:ECcommentClose = '$'
    elseif fileType =~ '^\(catalog\|sgmldecl\)$'
	let b:ECcommentOpen = '--'
	let b:ECcommentClose = '--'
    elseif fileType == 'dtml'
	let b:ECcommentOpen = '<dtml-comment>'
	let b:ECcommentClose = '</dtml-comment>'
    elseif fileType == 'htmlos'
	let b:ECcommentOpen = '#'
	let b:ECcommentClose = '/#'

Do you remember the section of your syntax script? The one that looks like this:

let b:current_syntax = "celstc"

Note that in the aforementioned code we have a variable fileType. We simply have to add our new file type "celstc" to this file. Along with our comment type, which starts with a # and ends at the end of line.

Code Snippet 3
    elseif fileType == 'atlas'
	let b:ECcommentOpen = 'C'
	let b:ECcommentClose = '$'
    elseif fileType =~ '^\(catalog\|sgmldecl\)$'
	let b:ECcommentOpen = '--'
	let b:ECcommentClose = '--'
    elseif fileType == 'dtml'
	let b:ECcommentOpen = '<dtml-comment>'
	let b:ECcommentClose = '</dtml-comment>'
    elseif fileType == 'htmlos'
	let b:ECcommentOpen = '#'
	let b:ECcommentClose = '/#'

    elseif fileType == 'celstc'
	let b:ECcommentOpen = '#'
	let b:ECcommentClose = ''

If you noticed I added the following code to "Code Snippet 2".

elseif fileType == 'celstc'
	let b:ECcommentOpen = '#'
	let b:ECcommentClose = ''

You could also make this code match multiple file types. Lets say I had multiple file types like celstsc, celssc, and celdsc. You could use a regular expression as follows to match all of those types.

elseif fileType =~ '^\(celstsc\|celssc\|celdsc\)$'
	let b:ECcommentOpen = '--'
	let b:ECcommentClose = '--'

Note the change in the operator from == to =~. I also am placing each filetype encased in pipes (|) and parenthesis.

Now just save that file, and you can use the block commenting on your new celestia (.stc) file type. <F6>, Shift+<F6>.

Link title

See also

Comments

 TO DO 

  • This new tip is not quite complete. Clearmoments intends to make further changes soon. Others are of course welcome to contribute as well.
  • What is intended for [[Link title]]?
  • How about replacing celstcStarBlockCmd with a simpler name?

Also mention using the $HOME/vimfiles/syntax (or $HOME/.vim/syntax) directory for syntax files that not everyone on the system will need. Less likely to be "blown away" by accident, and it won't need to be copied every version of Vim that comes out.

--Fritzophrenic 13:15, 29 April 2008 (UTC)


Thanks for the great recommendations. I just edited the page, I am very pleased about the reaction; frankly I thought it wasn't going to be as good. Feel free to make all the changes in the world. This is no longer my work, it belongs to the community, though I intend to make further changes. Clearmoments 00:29, 30 April 2008 (UTC)


I am debating adding the EnhancedCommentify section as a separate article or to leave it as part of the article. Please comment.

Clearmoments 02:45, 30 April 2008 (UTC)