]> git.saurik.com Git - wxWidgets.git/blobdiff - src/stc/scintilla/lexers/LexCoffeeScript.cxx
Initial copy of Scintilla 3.21 code
[wxWidgets.git] / src / stc / scintilla / lexers / LexCoffeeScript.cxx
diff --git a/src/stc/scintilla/lexers/LexCoffeeScript.cxx b/src/stc/scintilla/lexers/LexCoffeeScript.cxx
new file mode 100644 (file)
index 0000000..96d5d2d
--- /dev/null
@@ -0,0 +1,571 @@
+// Scintilla source code edit control
+/** @file LexCoffeeScript.cxx
+ ** Lexer for CoffeeScript.
+ **/
+// Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>
+// Based on the Scintilla C++ Lexer
+// Written by Eric Promislow <ericp@activestate.com> in 2011 for the Komodo IDE
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "Platform.h"
+#include "ILexer.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+#include "WordList.h"
+#include "LexAccessor.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "CharacterSet.h"
+#include "LexerModule.h"
+
+#ifdef SCI_NAMESPACE
+using namespace Scintilla;
+#endif
+
+static bool IsSpaceEquiv(int state) {
+       return (state <= SCE_C_COMMENTDOC
+           // including SCE_C_DEFAULT, SCE_C_COMMENT, SCE_C_COMMENTLINE
+           || state == SCE_C_COMMENTLINEDOC
+           || state == SCE_C_COMMENTDOCKEYWORD
+           || state == SCE_C_COMMENTDOCKEYWORDERROR
+           || state == SCE_COFFEESCRIPT_COMMENTBLOCK
+           || state == SCE_COFFEESCRIPT_VERBOSE_REGEX
+           || state == SCE_COFFEESCRIPT_VERBOSE_REGEX_COMMENT
+           || state == SCE_C_WORD
+           || state == SCE_C_REGEX);
+}
+
+// Preconditions: sc.currentPos points to a character after '+' or '-'.
+// The test for pos reaching 0 should be redundant,
+// and is in only for safety measures.
+// Limitation: this code will give the incorrect answer for code like
+// a = b+++/ptn/...
+// Putting a space between the '++' post-inc operator and the '+' binary op
+// fixes this, and is highly recommended for readability anyway.
+static bool FollowsPostfixOperator(StyleContext &sc, Accessor &styler) {
+       int pos = (int) sc.currentPos;
+       while (--pos > 0) {
+               char ch = styler[pos];
+               if (ch == '+' || ch == '-') {
+                       return styler[pos - 1] == ch;
+               }
+       }
+       return false;
+}
+
+static bool followsReturnKeyword(StyleContext &sc, Accessor &styler) {
+    // Don't look at styles, so no need to flush.
+       int pos = (int) sc.currentPos;
+       int currentLine = styler.GetLine(pos);
+       int lineStartPos = styler.LineStart(currentLine);
+       char ch;
+       while (--pos > lineStartPos) {
+               ch = styler.SafeGetCharAt(pos);
+               if (ch != ' ' && ch != '\t') {
+                       break;
+               }
+       }
+       const char *retBack = "nruter";
+       const char *s = retBack;
+       while (*s
+              && pos >= lineStartPos
+              && styler.SafeGetCharAt(pos) == *s) {
+               s++;
+               pos--;
+       }
+       return !*s;
+}
+
+static void ColouriseCoffeeScriptDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+                            Accessor &styler) {
+
+       WordList &keywords = *keywordlists[0];
+       WordList &keywords2 = *keywordlists[1];
+       WordList &keywords3 = *keywordlists[2];
+       WordList &keywords4 = *keywordlists[3];
+
+       // property styling.within.preprocessor
+       //      For C++ code, determines whether all preprocessor code is styled in the preprocessor style (0, the default)
+       //      or only from the initial # to the end of the command word(1).
+       bool stylingWithinPreprocessor = styler.GetPropertyInt("styling.within.preprocessor") != 0;
+
+       CharacterSet setOKBeforeRE(CharacterSet::setNone, "([{=,:;!%^&*|?~+-");
+       CharacterSet setCouldBePostOp(CharacterSet::setNone, "+-");
+
+       CharacterSet setDoxygen(CharacterSet::setAlpha, "$@\\&<>#{}[]");
+
+       CharacterSet setWordStart(CharacterSet::setAlpha, "_", 0x80, true);
+       CharacterSet setWord(CharacterSet::setAlphaNum, "._", 0x80, true);
+
+       // property lexer.cpp.allow.dollars
+       //      Set to 0 to disallow the '$' character in identifiers with the cpp lexer.
+       if (styler.GetPropertyInt("lexer.cpp.allow.dollars", 1) != 0) {
+               setWordStart.Add('$');
+               setWord.Add('$');
+       }
+
+       int chPrevNonWhite = ' ';
+       int visibleChars = 0;
+       bool lastWordWasUUID = false;
+       int styleBeforeDCKeyword = SCE_C_DEFAULT;
+       bool continuationLine = false;
+       bool isIncludePreprocessor = false;
+
+       if (initStyle == SCE_C_PREPROCESSOR) {
+               // Set continuationLine if last character of previous line is '\'
+               int lineCurrent = styler.GetLine(startPos);
+               if (lineCurrent > 0) {
+                       int chBack = styler.SafeGetCharAt(startPos-1, 0);
+                       int chBack2 = styler.SafeGetCharAt(startPos-2, 0);
+                       int lineEndChar = '!';
+                       if (chBack2 == '\r' && chBack == '\n') {
+                               lineEndChar = styler.SafeGetCharAt(startPos-3, 0);
+                       } else if (chBack == '\n' || chBack == '\r') {
+                               lineEndChar = chBack2;
+                       }
+                       continuationLine = lineEndChar == '\\';
+               }
+       }
+
+       // look back to set chPrevNonWhite properly for better regex colouring
+       int endPos = startPos + length;
+       if (startPos > 0) {
+               unsigned int back = startPos;
+               styler.Flush();
+               while (back > 0 && IsSpaceEquiv(styler.StyleAt(--back)))
+                       ;
+               if (styler.StyleAt(back) == SCE_C_OPERATOR) {
+                       chPrevNonWhite = styler.SafeGetCharAt(back);
+               }
+               if (startPos != back) {
+                       initStyle = styler.StyleAt(back);
+               }
+               startPos = back;
+       }
+
+       StyleContext sc(startPos, endPos - startPos, initStyle, styler);
+
+       for (; sc.More(); sc.Forward()) {
+
+               if (sc.atLineStart) {
+                       // Reset states to begining of colourise so no surprises
+                       // if different sets of lines lexed.
+                       visibleChars = 0;
+                       lastWordWasUUID = false;
+                       isIncludePreprocessor = false;
+               }
+
+               // Handle line continuation generically.
+               if (sc.ch == '\\') {
+                       if (sc.chNext == '\n' || sc.chNext == '\r') {
+                               sc.Forward();
+                               if (sc.ch == '\r' && sc.chNext == '\n') {
+                                       sc.Forward();
+                               }
+                               continuationLine = true;
+                               continue;
+                       }
+               }
+
+               // Determine if the current state should terminate.
+               switch (sc.state) {
+                       case SCE_C_OPERATOR:
+                               sc.SetState(SCE_C_DEFAULT);
+                               break;
+                       case SCE_C_NUMBER:
+                               // We accept almost anything because of hex. and number suffixes
+                               if (!setWord.Contains(sc.ch)) {
+                                       sc.SetState(SCE_C_DEFAULT);
+                               }
+                               break;
+                       case SCE_C_IDENTIFIER:
+                               if (!setWord.Contains(sc.ch) || (sc.ch == '.') || (sc.ch == '$')) {
+                                       char s[1000];
+                                       sc.GetCurrent(s, sizeof(s));
+                                       if (keywords.InList(s)) {
+                                               lastWordWasUUID = strcmp(s, "uuid") == 0;
+                                               sc.ChangeState(SCE_C_WORD);
+                                       } else if (keywords2.InList(s)) {
+                                               sc.ChangeState(SCE_C_WORD2);
+                                       } else if (keywords4.InList(s)) {
+                                               sc.ChangeState(SCE_C_GLOBALCLASS);
+                                       }
+                                       sc.SetState(SCE_C_DEFAULT);
+                               }
+                               break;
+                       case SCE_C_PREPROCESSOR:
+                               if (sc.atLineStart && !continuationLine) {
+                                       sc.SetState(SCE_C_DEFAULT);
+                               } else if (stylingWithinPreprocessor) {
+                                       if (IsASpace(sc.ch)) {
+                                               sc.SetState(SCE_C_DEFAULT);
+                                       }
+                               } else {
+                                       if (sc.Match('/', '*') || sc.Match('/', '/')) {
+                                               sc.SetState(SCE_C_DEFAULT);
+                                       }
+                               }
+                               break;
+                       case SCE_C_COMMENT:
+                               if (sc.Match('*', '/')) {
+                                       sc.Forward();
+                                       sc.ForwardSetState(SCE_C_DEFAULT);
+                               }
+                               break;
+                       case SCE_C_COMMENTDOC:
+                               if (sc.Match('*', '/')) {
+                                       sc.Forward();
+                                       sc.ForwardSetState(SCE_C_DEFAULT);
+                               } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support
+                                       // Verify that we have the conditions to mark a comment-doc-keyword
+                                       if ((IsASpace(sc.chPrev) || sc.chPrev == '*') && (!IsASpace(sc.chNext))) {
+                                               styleBeforeDCKeyword = SCE_C_COMMENTDOC;
+                                               sc.SetState(SCE_C_COMMENTDOCKEYWORD);
+                                       }
+                               }
+                               break;
+                       case SCE_C_COMMENTLINE:
+                               if (sc.atLineStart) {
+                                       sc.SetState(SCE_C_DEFAULT);
+                               }
+                               break;
+                       case SCE_C_COMMENTLINEDOC:
+                               if (sc.atLineStart) {
+                                       sc.SetState(SCE_C_DEFAULT);
+                               } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support
+                                       // Verify that we have the conditions to mark a comment-doc-keyword
+                                       if ((IsASpace(sc.chPrev) || sc.chPrev == '/' || sc.chPrev == '!') && (!IsASpace(sc.chNext))) {
+                                               styleBeforeDCKeyword = SCE_C_COMMENTLINEDOC;
+                                               sc.SetState(SCE_C_COMMENTDOCKEYWORD);
+                                       }
+                               }
+                               break;
+                       case SCE_C_COMMENTDOCKEYWORD:
+                               if ((styleBeforeDCKeyword == SCE_C_COMMENTDOC) && sc.Match('*', '/')) {
+                                       sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR);
+                                       sc.Forward();
+                                       sc.ForwardSetState(SCE_C_DEFAULT);
+                               } else if (!setDoxygen.Contains(sc.ch)) {
+                                       char s[100];
+                                       sc.GetCurrent(s, sizeof(s));
+                                       if (!IsASpace(sc.ch) || !keywords3.InList(s + 1)) {
+                                               sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR);
+                                       }
+                                       sc.SetState(styleBeforeDCKeyword);
+                               }
+                               break;
+                       case SCE_C_STRING:
+                               if (isIncludePreprocessor) {
+                                       if (sc.ch == '>') {
+                                               sc.ForwardSetState(SCE_C_DEFAULT);
+                                               isIncludePreprocessor = false;
+                                       }
+                               } else if (sc.ch == '\\') {
+                                       if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
+                                               sc.Forward();
+                                       }
+                               } else if (sc.ch == '\"') {
+                                       sc.ForwardSetState(SCE_C_DEFAULT);
+                               }
+                               break;
+                       case SCE_C_CHARACTER:
+                               if (sc.ch == '\\') {
+                                       if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
+                                               sc.Forward();
+                                       }
+                               } else if (sc.ch == '\'') {
+                                       sc.ForwardSetState(SCE_C_DEFAULT);
+                               }
+                               break;
+                       case SCE_C_REGEX:
+                               if (sc.atLineStart) {
+                                       sc.SetState(SCE_C_DEFAULT);
+                               } else if (sc.ch == '/') {
+                                       sc.Forward();
+                                       while ((sc.ch < 0x80) && islower(sc.ch))
+                                               sc.Forward();    // gobble regex flags
+                                       sc.SetState(SCE_C_DEFAULT);
+                               } else if (sc.ch == '\\') {
+                                       // Gobble up the quoted character
+                                       if (sc.chNext == '\\' || sc.chNext == '/') {
+                                               sc.Forward();
+                                       }
+                               }
+                               break;
+                       case SCE_C_STRINGEOL:
+                               if (sc.atLineStart) {
+                                       sc.SetState(SCE_C_DEFAULT);
+                               }
+                               break;
+                       case SCE_C_VERBATIM:
+                               if (sc.ch == '\"') {
+                                       if (sc.chNext == '\"') {
+                                               sc.Forward();
+                                       } else {
+                                               sc.ForwardSetState(SCE_C_DEFAULT);
+                                       }
+                               }
+                               break;
+                       case SCE_C_UUID:
+                               if (sc.ch == '\r' || sc.ch == '\n' || sc.ch == ')') {
+                                       sc.SetState(SCE_C_DEFAULT);
+                               }
+                               break;
+                       case SCE_COFFEESCRIPT_COMMENTBLOCK:
+                               if (sc.Match("###")) {
+                                       sc.ChangeState(SCE_C_COMMENT);
+                                       sc.Forward();
+                                       sc.Forward();
+                                       sc.ForwardSetState(SCE_C_DEFAULT);
+                               } else if (sc.ch == '\\') {
+                                       sc.Forward();
+                               }
+                               break;
+                       case SCE_COFFEESCRIPT_VERBOSE_REGEX:
+                               if (sc.Match("///")) {
+                                       sc.Forward();
+                                       sc.Forward();
+                                       sc.ChangeState(SCE_C_REGEX);
+                                       sc.ForwardSetState(SCE_C_DEFAULT);
+                               } else if (sc.Match('#')) {
+                                       sc.ChangeState(SCE_C_REGEX);
+                                       sc.SetState(SCE_COFFEESCRIPT_VERBOSE_REGEX_COMMENT);
+                               } else if (sc.ch == '\\') {
+                                       sc.Forward();
+                               }
+                               break;
+                       case SCE_COFFEESCRIPT_VERBOSE_REGEX_COMMENT:
+                               if (sc.atLineStart) {
+                                       sc.ChangeState(SCE_C_COMMENT);
+                                       sc.SetState(SCE_COFFEESCRIPT_VERBOSE_REGEX);
+                               }
+                               break;
+               }
+
+               // Determine if a new state should be entered.
+               if (sc.state == SCE_C_DEFAULT) {
+                       if (sc.Match('@', '\"')) {
+                               sc.SetState(SCE_C_VERBATIM);
+                               sc.Forward();
+                       } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
+                               if (lastWordWasUUID) {
+                                       sc.SetState(SCE_C_UUID);
+                                       lastWordWasUUID = false;
+                               } else {
+                                       sc.SetState(SCE_C_NUMBER);
+                               }
+                       } else if (setWordStart.Contains(sc.ch) || (sc.ch == '@') || (sc.ch == '$')) {
+                               if (lastWordWasUUID) {
+                                       sc.SetState(SCE_C_UUID);
+                                       lastWordWasUUID = false;
+                               } else {
+                                       sc.SetState(SCE_C_IDENTIFIER);
+                               }
+                       } else if (sc.Match('/', '*')) {
+                               if (sc.Match("/**") || sc.Match("/*!")) {       // Support of Qt/Doxygen doc. style
+                                       sc.SetState(SCE_C_COMMENTDOC);
+                               } else {
+                                       sc.SetState(SCE_C_COMMENT);
+                               }
+                               sc.Forward();   // Eat the * so it isn't used for the end of the comment
+                       } else if (sc.Match("///")) {
+                               sc.SetState(SCE_COFFEESCRIPT_VERBOSE_REGEX);
+                       } else if (sc.ch == '/'
+                                  && (setOKBeforeRE.Contains(chPrevNonWhite)
+                                      || followsReturnKeyword(sc, styler))
+                                  && (!setCouldBePostOp.Contains(chPrevNonWhite)
+                                      || !FollowsPostfixOperator(sc, styler))) {
+                               sc.SetState(SCE_C_REGEX);       // JavaScript's RegEx
+                       } else if (sc.ch == '\"') {
+                               sc.SetState(SCE_C_STRING);
+                               isIncludePreprocessor = false;  // ensure that '>' won't end the string
+                       } else if (isIncludePreprocessor && sc.ch == '<') {
+                               sc.SetState(SCE_C_STRING);
+                       } else if (sc.ch == '\'') {
+                               sc.SetState(SCE_C_CHARACTER);
+                       } else if (sc.ch == '#') {
+                if (sc.Match("###")) {
+                    sc.SetState(SCE_COFFEESCRIPT_COMMENTBLOCK);
+                } else {
+                    sc.SetState(SCE_C_COMMENTLINE);
+                }
+                       } else if (isoperator(static_cast<char>(sc.ch))) {
+                               sc.SetState(SCE_C_OPERATOR);
+                       }
+               }
+
+               if (!IsASpace(sc.ch) && !IsSpaceEquiv(sc.state)) {
+                       chPrevNonWhite = sc.ch;
+                       visibleChars++;
+               }
+               continuationLine = false;
+       }
+    // Change temporary coffeescript states into standard C ones.
+    switch (sc.state) {
+        case SCE_COFFEESCRIPT_COMMENTBLOCK:
+            sc.ChangeState(SCE_C_COMMENT);
+            break;
+        case SCE_COFFEESCRIPT_VERBOSE_REGEX:
+            sc.ChangeState(SCE_C_REGEX);
+            break;
+        case SCE_COFFEESCRIPT_VERBOSE_REGEX_COMMENT:
+            sc.ChangeState(SCE_C_COMMENTLINE);
+            break;
+    }
+       sc.Complete();
+}
+
+static bool IsCommentLine(int line, Accessor &styler) {
+       int pos = styler.LineStart(line);
+       int eol_pos = styler.LineStart(line + 1) - 1;
+       for (int i = pos; i < eol_pos; i++) {
+               char ch = styler[i];
+               if (ch == '#')
+                       return true;
+        else if (ch == '/'
+                 && i < eol_pos - 1
+                 && styler[i + 1] == '*')
+                       return true;
+               else if (ch != ' ' && ch != '\t')
+                       return false;
+       }
+       return false;
+}
+
+static void FoldCoffeeScriptDoc(unsigned int startPos, int length, int,
+                               WordList *[], Accessor &styler) {
+       // A simplified version of FoldPyDoc
+       const int maxPos = startPos + length;
+       const int maxLines = styler.GetLine(maxPos - 1);             // Requested last line
+       const int docLines = styler.GetLine(styler.Length() - 1);  // Available last line
+
+       // property fold.coffeescript.comment
+       const bool foldComment = styler.GetPropertyInt("fold.coffeescript.comment") != 0;
+
+       const bool foldCompact = styler.GetPropertyInt("fold.compact") != 0;
+
+       // Backtrack to previous non-blank line so we can determine indent level
+       // for any white space lines
+       // and so we can fix any preceding fold level (which is why we go back
+       // at least one line in all cases)
+       int spaceFlags = 0;
+       int lineCurrent = styler.GetLine(startPos);
+       int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, NULL);
+       while (lineCurrent > 0) {
+               lineCurrent--;
+               indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, NULL);
+               if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)
+                   && !IsCommentLine(lineCurrent, styler))
+                       break;
+       }
+       int indentCurrentLevel = indentCurrent & SC_FOLDLEVELNUMBERMASK;
+
+       // Set up initial loop state
+       int prevComment = 0;
+       if (lineCurrent >= 1)
+               prevComment = foldComment && IsCommentLine(lineCurrent - 1, styler);
+
+       // Process all characters to end of requested range
+       // or comment that hangs over the end of the range.  Cap processing in all cases
+       // to end of document (in case of comment at end).
+       while ((lineCurrent <= docLines) && ((lineCurrent <= maxLines) || prevComment)) {
+
+               // Gather info
+               int lev = indentCurrent;
+               int lineNext = lineCurrent + 1;
+               int indentNext = indentCurrent;
+               if (lineNext <= docLines) {
+                       // Information about next line is only available if not at end of document
+                       indentNext = styler.IndentAmount(lineNext, &spaceFlags, NULL);
+               }
+               const int comment = foldComment && IsCommentLine(lineCurrent, styler);
+               const int comment_start = (comment && !prevComment && (lineNext <= docLines) &&
+                                          IsCommentLine(lineNext, styler) && (lev > SC_FOLDLEVELBASE));
+               const int comment_continue = (comment && prevComment);
+               if (!comment)
+                       indentCurrentLevel = indentCurrent & SC_FOLDLEVELNUMBERMASK;
+               if (indentNext & SC_FOLDLEVELWHITEFLAG)
+                       indentNext = SC_FOLDLEVELWHITEFLAG | indentCurrentLevel;
+
+               if (comment_start) {
+                       // Place fold point at start of a block of comments
+                       lev |= SC_FOLDLEVELHEADERFLAG;
+               } else if (comment_continue) {
+                       // Add level to rest of lines in the block
+                       lev = lev + 1;
+               }
+
+               // Skip past any blank lines for next indent level info; we skip also
+               // comments (all comments, not just those starting in column 0)
+               // which effectively folds them into surrounding code rather
+               // than screwing up folding.
+
+               while ((lineNext < docLines) &&
+                       ((indentNext & SC_FOLDLEVELWHITEFLAG) ||
+                        (lineNext <= docLines && IsCommentLine(lineNext, styler)))) {
+
+                       lineNext++;
+                       indentNext = styler.IndentAmount(lineNext, &spaceFlags, NULL);
+               }
+
+               const int levelAfterComments = indentNext & SC_FOLDLEVELNUMBERMASK;
+               const int levelBeforeComments = Platform::Maximum(indentCurrentLevel,levelAfterComments);
+
+               // Now set all the indent levels on the lines we skipped
+               // Do this from end to start.  Once we encounter one line
+               // which is indented more than the line after the end of
+               // the comment-block, use the level of the block before
+
+               int skipLine = lineNext;
+               int skipLevel = levelAfterComments;
+
+               while (--skipLine > lineCurrent) {
+                       int skipLineIndent = styler.IndentAmount(skipLine, &spaceFlags, NULL);
+
+                       if (foldCompact) {
+                               if ((skipLineIndent & SC_FOLDLEVELNUMBERMASK) > levelAfterComments)
+                                       skipLevel = levelBeforeComments;
+
+                               int whiteFlag = skipLineIndent & SC_FOLDLEVELWHITEFLAG;
+
+                               styler.SetLevel(skipLine, skipLevel | whiteFlag);
+                       } else {
+                               if ((skipLineIndent & SC_FOLDLEVELNUMBERMASK) > levelAfterComments &&
+                                       !(skipLineIndent & SC_FOLDLEVELWHITEFLAG) &&
+                                       !IsCommentLine(skipLine, styler))
+                                       skipLevel = levelBeforeComments;
+
+                               styler.SetLevel(skipLine, skipLevel);
+                       }
+               }
+
+               // Set fold header on non-comment line
+               if (!comment && !(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
+                       if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK))
+                               lev |= SC_FOLDLEVELHEADERFLAG;
+               }
+
+               // Keep track of block comment state of previous line
+               prevComment = comment_start || comment_continue;
+
+               // Set fold level for this line and move to next line
+               styler.SetLevel(lineCurrent, lev);
+               indentCurrent = indentNext;
+               lineCurrent = lineNext;
+       }
+}
+
+static const char *const csWordLists[] = {
+            "Keywords",
+            0,
+};
+
+LexerModule lmCoffeeScript(SCLEX_COFFEESCRIPT, ColouriseCoffeeScriptDoc, "coffeescript", FoldCoffeeScriptDoc, csWordLists);