diff options
Diffstat (limited to 'lexers/LexHaskell.cxx')
| -rw-r--r-- | lexers/LexHaskell.cxx | 360 | 
1 files changed, 235 insertions, 125 deletions
| diff --git a/lexers/LexHaskell.cxx b/lexers/LexHaskell.cxx index f48b7bab9..3e0f83d5b 100644 --- a/lexers/LexHaskell.cxx +++ b/lexers/LexHaskell.cxx @@ -13,7 +13,7 @@   *    Improvements by kudah <kudahkukarek@gmail.com>   *   *    TODO: - *    * Implement a folder :) + *    * Fold group declarations, comments, pragmas, #ifdefs, explicit layout, lists, tuples, quasi-quotes, splces, etc, etc, etc.   *    * Nice Character-lexing (stuff inside '\''), LexPython has   *      this.   * @@ -26,6 +26,9 @@  #include <assert.h>  #include <ctype.h> +#include <string> +#include <map> +  #include "ILexer.h"  #include "Scintilla.h"  #include "SciLexer.h" @@ -37,20 +40,12 @@  #include "StyleContext.h"  #include "CharacterSet.h"  #include "LexerModule.h" +#include "OptionSet.h"  #ifdef SCI_NAMESPACE  using namespace Scintilla;  #endif -#ifdef BUILD_AS_EXTERNAL_LEXER - -#include "ExternalLexer.h" -#include "WindowAccessor.h" - -#define BUILD_EXTERNAL_LEXER 0 - -#endif -  #define HA_MODE_DEFAULT     0  #define HA_MODE_IMPORT1     1  #define HA_MODE_IMPORT2     2 @@ -87,35 +82,166 @@ static inline void skipMagicHash(StyleContext &sc, const bool magicHash, const b     }  } -static void ColorizeHaskellDoc(unsigned int startPos, int length, int initStyle, -                               WordList *keywordlists[], Accessor &styler) { - -   WordList &keywords = *keywordlists[0]; -   WordList &ffi      = *keywordlists[1]; - -   // property lexer.haskell.allow.hash -   //  Set to 1 to allow the # character in identifiers and literals with the -   //  haskell lexer. -   //  (GHC -XMagicHash extension) -   const bool magicHash = styler.GetPropertyInt("lexer.haskell.allow.hash") != 0; -   // property lexer.haskell.allow.quotes -   //  Set to 1 to enable highlighting of Template Haskell name quotations -   //  and promoted constructors -   //  (GHC -XTemplateHaskell and -XDataKinds extensions) -   const bool allowQuotes = styler.GetPropertyInt("lexer.haskell.allow.quotes") != 0; -   // property lexer.haskell.import.safe -   //  Set to 1 to allow keyword "safe" in imports -   //  (GHC SafeHaskell extensions) -   const bool highlightSafe = styler.GetPropertyInt("lexer.haskell.import.safe") != 0; -   const bool stylingWithinPreprocessor = styler.GetPropertyInt("styling.within.preprocessor") != 0; +struct OptionsHaskell { +   bool magicHash; +   bool allowQuotes; +   bool highlightSafe; +   bool stylingWithinPreprocessor; +   bool fold; +   bool foldComment; +   bool foldCompact; +   bool foldImports; +   OptionsHaskell() { +      magicHash = true; +      allowQuotes = true; +      highlightSafe = true; +      stylingWithinPreprocessor = false; +      fold = false; +      foldComment = false; +      foldCompact = false; +      foldImports = false; +   } +}; + +static const char * const haskellWordListDesc[] = { +   "Keywords", +   "FFI", +   0 +}; + +struct OptionSetHaskell : public OptionSet<OptionsHaskell> { +   OptionSetHaskell() { +      DefineProperty("lexer.haskell.allow.hash", &OptionsHaskell::magicHash, +         "Set to 1 to allow the '#' character at the end of identifiers and " +         "literals with the haskell lexer (GHC -XMagicHash extension)"); + +      DefineProperty("lexer.haskell.allow.quotes", &OptionsHaskell::allowQuotes, +         "Set to 1 to enable highlighting of Template Haskell name quotations " +         "and promoted constructors " +         "(GHC -XTemplateHaskell and -XDataKinds extensions)"); + +      DefineProperty("lexer.haskell.import.safe", &OptionsHaskell::highlightSafe, +         "Set to 1 to allow keyword \"safe\" in imports " +         "(GHC SafeHaskell extensions)"); + +      DefineProperty("styling.within.preprocessor", &OptionsHaskell::stylingWithinPreprocessor, +         "For Haskell 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)." +         ); + +      DefineProperty("fold", &OptionsHaskell::fold); + +      DefineProperty("fold.comment", &OptionsHaskell::foldComment); + +      DefineProperty("fold.compact", &OptionsHaskell::foldCompact); + +      DefineProperty("fold.haskell.imports", &OptionsHaskell::foldImports, +         "Set to 1 to enable folding of import declarations"); + +      DefineWordListSets(haskellWordListDesc); +   } +}; + +class LexerHaskell : public ILexer { +   int firstImportLine; +   WordList keywords; +   WordList ffi; +   OptionsHaskell options; +   OptionSetHaskell osHaskell; + +   inline bool LineContainsImport(int line, Accessor &styler) { +      if (options.foldImports) { +         return styler.Match(styler.LineStart(line), "import"); +      } else { +         return false; +      } +   } +public: +   LexerHaskell() : firstImportLine(-1) {} +   virtual ~LexerHaskell() {} + +   void SCI_METHOD Release() { +      delete this; +   } + +   int SCI_METHOD Version() const { +      return lvOriginal; +   } + +   const char * SCI_METHOD PropertyNames() { +      return osHaskell.PropertyNames(); +   } + +   int SCI_METHOD PropertyType(const char *name) { +      return osHaskell.PropertyType(name); +   } + +   const char * SCI_METHOD DescribeProperty(const char *name) { +      return osHaskell.DescribeProperty(name); +   } + +   int SCI_METHOD PropertySet(const char *key, const char *val); + +   const char * SCI_METHOD DescribeWordListSets() { +      return osHaskell.DescribeWordListSets(); +   } + +   int SCI_METHOD WordListSet(int n, const char *wl); + +   void SCI_METHOD Lex(unsigned int startPos, int length, int initStyle, IDocument *pAccess); + +   void SCI_METHOD Fold(unsigned int startPos, int length, int initStyle, IDocument *pAccess); + +   void * SCI_METHOD PrivateCall(int, void *) { +      return 0; +   } + +   static ILexer *LexerFactoryHaskell() { +      return new LexerHaskell(); +   } +}; + +int SCI_METHOD LexerHaskell::PropertySet(const char *key, const char *val) { +   if (osHaskell.PropertySet(&options, key, val)) { +      return 0; +   } +   return -1; +} + +int SCI_METHOD LexerHaskell::WordListSet(int n, const char *wl) { +   WordList *wordListN = 0; +   switch (n) { +   case 0: +      wordListN = &keywords; +      break; +   case 1: +      wordListN = &ffi; +      break; +   } +   int firstModification = -1; +   if (wordListN) { +      WordList wlNew; +      wlNew.Set(wl); +      if (*wordListN != wlNew) { +         wordListN->Set(wl); +         firstModification = 0; +      } +   } +   return firstModification; +} + +void SCI_METHOD LexerHaskell::Lex(unsigned int startPos, int length, int initStyle +                                 ,IDocument *pAccess) { +   LexAccessor styler(pAccess);     StyleContext sc(startPos, length, initStyle, styler);     int lineCurrent = styler.GetLine(startPos);     int state = lineCurrent ? styler.GetLineState(lineCurrent-1) : 0; -   int mode  = state & 0xF; -   int nestLevel = state >> 4; +   int mode  = state & 0x7; +   int nestLevel = state >> 3;     int base = 10;     bool inDashes = false; @@ -126,10 +252,10 @@ static void ColorizeHaskellDoc(unsigned int startPos, int length, int initStyle,        // For line numbering (and by extension, nested comments) to work,        // states should either only forward one character at a time, or check        // that characters they're skipping are not newlines. If states match on -      // line end, they should skip it, to prevent double counting. +      // line end, they should skip it to prevent double counting.        if (sc.atLineEnd) {           // Remember the line state for future incremental lexing -         styler.SetLineState(lineCurrent, (nestLevel << 4) | mode); +         styler.SetLineState(lineCurrent, (nestLevel << 3) | mode);           lineCurrent++;        } @@ -139,7 +265,7 @@ static void ColorizeHaskellDoc(unsigned int startPos, int length, int initStyle,           || sc.state == SCE_HA_PREPROCESSOR)) {           if (sc.chNext == '\n' || sc.chNext == '\r') {              // Remember the line state for future incremental lexing -            styler.SetLineState(lineCurrent, (nestLevel << 4) | mode); +            styler.SetLineState(lineCurrent, (nestLevel << 3) | mode);              lineCurrent++;              sc.Forward(); @@ -171,7 +297,7 @@ static void ColorizeHaskellDoc(unsigned int startPos, int length, int initStyle,        else if (sc.state == SCE_HA_STRING) {           if (sc.ch == '\"') {              sc.Forward(); -            skipMagicHash(sc, magicHash, false); +            skipMagicHash(sc, options.magicHash, false);              sc.SetState(SCE_HA_DEFAULT);           } else if (sc.ch == '\\') {              sc.Forward(2); @@ -186,7 +312,7 @@ static void ColorizeHaskellDoc(unsigned int startPos, int length, int initStyle,        else if (sc.state == SCE_HA_CHARACTER) {           if (sc.ch == '\'') {              sc.Forward(); -            skipMagicHash(sc, magicHash, false); +            skipMagicHash(sc, options.magicHash, false);              sc.SetState(SCE_HA_DEFAULT);           } else if (sc.ch == '\\') {              sc.Forward(2); @@ -209,7 +335,7 @@ static void ColorizeHaskellDoc(unsigned int startPos, int length, int initStyle,              if (sc.ch == '+' || sc.ch == '-')                  sc.Forward();           } else { -            skipMagicHash(sc, magicHash, true); +            skipMagicHash(sc, options.magicHash, true);              sc.SetState(SCE_HA_DEFAULT);           }        } @@ -222,7 +348,7 @@ static void ColorizeHaskellDoc(unsigned int startPos, int length, int initStyle,           while (sc.More()) {              if (IsAWordChar(sc.ch)) {                 sc.Forward(); -            } else if (sc.ch == '#' && magicHash) { +            } else if (sc.ch == '#' && options.magicHash) {                 sc.Forward();                 break;              } else if (style == SCE_HA_CAPITAL && sc.ch=='.') { @@ -264,7 +390,7 @@ static void ColorizeHaskellDoc(unsigned int startPos, int length, int initStyle,                      strcmp(s,"qualified") == 0) {               style    = SCE_HA_KEYWORD;               new_mode = HA_MODE_IMPORT1; -         } else if (highlightSafe && +         } else if (options.highlightSafe &&                      mode == HA_MODE_IMPORT1 &&                      strcmp(s,"safe") == 0) {               style    = SCE_HA_KEYWORD; @@ -346,7 +472,7 @@ static void ColorizeHaskellDoc(unsigned int startPos, int length, int initStyle,        }              // Preprocessor        else if (sc.state == SCE_HA_PREPROCESSOR) { -         if (stylingWithinPreprocessor && !IsAWordStart(sc.ch)) { +         if (options.stylingWithinPreprocessor && !IsAWordStart(sc.ch)) {              sc.SetState(SCE_HA_DEFAULT);           } else if (sc.atLineEnd) {              sc.SetState(SCE_HA_DEFAULT); @@ -403,7 +529,7 @@ static void ColorizeHaskellDoc(unsigned int startPos, int length, int initStyle,              int style = SCE_HA_CHARACTER; -            if (allowQuotes) { +            if (options.allowQuotes) {                 // Quoted type ''T                 if (sc.ch=='\'' && IsAWordStart(sc.chNext)) {                    sc.Forward(); @@ -457,16 +583,24 @@ static void ColorizeHaskellDoc(unsigned int startPos, int length, int initStyle,     sc.Complete();  } -static bool IsCommentLine(int line, Accessor &styler) { +static inline bool IsCommentStyle(int style) { +   return (style >= SCE_HA_COMMENTLINE && style <= SCE_HA_COMMENTBLOCK3); +} + +static bool LineStartsWithACommentOrPreprocessor(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++) { -      int ch = styler[i];        int style = styler.StyleAt(i); -      if ((style < SCE_HA_COMMENTLINE || style > SCE_HA_COMMENTBLOCK3) -         && ch != ' ' +      if (IsCommentStyle(style) || style == SCE_HA_PREPROCESSOR) { +         return true; +      } + +      int ch = styler[i]; + +      if (  ch != ' '           && ch != '\t') {           return false;        } @@ -474,8 +608,14 @@ static bool IsCommentLine(int line, Accessor &styler) {     return true;  } -static void FoldHaskellDoc(unsigned int startPos, int length, int // initStyle -                          ,WordList *[], Accessor &styler) { +void SCI_METHOD LexerHaskell::Fold(unsigned int startPos, int length, int // initStyle +                                  ,IDocument *pAccess) { +   if (!options.fold) +      return; + +   Accessor styler(pAccess, NULL); + +     const int maxPos = startPos + length;     const int maxLines =        maxPos == styler.Length() @@ -483,27 +623,39 @@ static void FoldHaskellDoc(unsigned int startPos, int length, int // initStyle           : styler.GetLine(maxPos - 1);  // Requested last line     const int docLines = styler.GetLine(styler.Length()); // Available last line -   const bool foldCompact = styler.GetPropertyInt("fold.compact") != 0; -   // const bool foldComment = styler.GetPropertyInt("fold.comment") != 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); +   bool importCurrent = LineContainsImport(lineCurrent, styler);     int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, NULL); +     while (lineCurrent > 0) {        lineCurrent--; +      importCurrent = LineContainsImport(lineCurrent, styler);        indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, NULL);        if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG) && -               !IsCommentLine(lineCurrent, styler)) +               !LineStartsWithACommentOrPreprocessor(lineCurrent, styler))           break;     } +     int indentCurrentLevel = indentCurrent & SC_FOLDLEVELNUMBERMASK; -   // Set up initial loop state -   startPos = styler.LineStart(lineCurrent); +   if (lineCurrent <= firstImportLine) { +      firstImportLine = -1; // readjust first import position +   } + +   if (importCurrent) { +      if (firstImportLine == -1) { +         firstImportLine = lineCurrent; +      } +      if (firstImportLine != lineCurrent) { +         indentCurrentLevel++; +         indentCurrent = indentCurrentLevel | (indentCurrent & ~SC_FOLDLEVELNUMBERMASK); +      } +   }     // Process all characters to end of requested range     //that hangs over the end of the range.  Cap processing in all cases @@ -511,11 +663,13 @@ static void FoldHaskellDoc(unsigned int startPos, int length, int // initStyle     while (lineCurrent <= docLines && lineCurrent <= maxLines) {        // Gather info -      int lev = indentCurrent;        int lineNext = lineCurrent + 1; +      bool importNext = LineContainsImport(lineNext, styler);        int indentNext = indentCurrent; +        if (lineNext <= docLines) {           // Information about next line is only available if not at end of document +         importNext = LineContainsImport(lineNext, styler);           indentNext = styler.IndentAmount(lineNext, &spaceFlags, NULL);        }        if (indentNext & SC_FOLDLEVELWHITEFLAG) @@ -528,13 +682,25 @@ static void FoldHaskellDoc(unsigned int startPos, int length, int // initStyle        while ((lineNext < docLines) &&              ((indentNext & SC_FOLDLEVELWHITEFLAG) || -             (lineNext <= docLines && IsCommentLine(lineNext, styler)))) { +             (lineNext <= docLines && LineStartsWithACommentOrPreprocessor(lineNext, styler)))) {           lineNext++; +         importNext = LineContainsImport(lineNext, styler);           indentNext = styler.IndentAmount(lineNext, &spaceFlags, NULL);        } -      const int levelAfterComments = indentNext & SC_FOLDLEVELNUMBERMASK; -      const int levelBeforeComments = Maximum(indentCurrentLevel,levelAfterComments); +      int indentNextLevel = indentNext & SC_FOLDLEVELNUMBERMASK; + +      if (importNext) { +         if (firstImportLine == -1) { +            firstImportLine = lineNext; +         } +         if (firstImportLine != lineNext) { +            indentNextLevel++; +            indentNext = indentNextLevel | (indentNext & ~SC_FOLDLEVELNUMBERMASK); +         } +      } + +      const int levelBeforeComments = Maximum(indentCurrentLevel,indentNextLevel);        // Now set all the indent levels on the lines we skipped        // Do this from end to start.  Once we encounter one line @@ -542,13 +708,13 @@ static void FoldHaskellDoc(unsigned int startPos, int length, int // initStyle        // the comment-block, use the level of the block before        int skipLine = lineNext; -      int skipLevel = levelAfterComments; +      int skipLevel = indentNextLevel;        while (--skipLine > lineCurrent) {           int skipLineIndent = styler.IndentAmount(skipLine, &spaceFlags, NULL); -         if (foldCompact) { -            if ((skipLineIndent & SC_FOLDLEVELNUMBERMASK) > levelAfterComments) { +         if (options.foldCompact) { +            if ((skipLineIndent & SC_FOLDLEVELNUMBERMASK) > indentNextLevel) {                 skipLevel = levelBeforeComments;              } @@ -556,9 +722,9 @@ static void FoldHaskellDoc(unsigned int startPos, int length, int // initStyle              styler.SetLevel(skipLine, skipLevel | whiteFlag);           } else { -            if (  (skipLineIndent & SC_FOLDLEVELNUMBERMASK) > levelAfterComments +            if (  (skipLineIndent & SC_FOLDLEVELNUMBERMASK) > indentNextLevel                 && !(skipLineIndent & SC_FOLDLEVELWHITEFLAG) -               && !IsCommentLine(skipLine, styler)) { +               && !LineStartsWithACommentOrPreprocessor(skipLine, styler)) {                 skipLevel = levelBeforeComments;              } @@ -566,15 +732,18 @@ static void FoldHaskellDoc(unsigned int startPos, int length, int // initStyle           }        } +      int lev = indentCurrent; +        if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {           if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK))              lev |= SC_FOLDLEVELHEADERFLAG;        }        // Set fold level for this line and move to next line -      styler.SetLevel(lineCurrent, foldCompact ? lev : lev & ~SC_FOLDLEVELWHITEFLAG); +      styler.SetLevel(lineCurrent, options.foldCompact ? lev : lev & ~SC_FOLDLEVELWHITEFLAG);        indentCurrent = indentNext;        lineCurrent = lineNext; +      importCurrent = importNext;     }     // NOTE: Cannot set level of last line here because indentCurrent doesn't have @@ -582,63 +751,4 @@ static void FoldHaskellDoc(unsigned int startPos, int length, int // initStyle     //styler.SetLevel(lineCurrent, indentCurrent);  } -static const char * const haskellWordListDesc[] = { -   "Keywords", -   "FFI", -   0 -}; - -// External stuff - used for dynamic-loading, not implemented in wxStyledTextCtrl yet. -// Inspired by the caml external lexer - Credits to Robert Roessler - http://www.rftp.com -#ifdef BUILD_EXTERNAL_LEXER -static const char* LexerName = "haskell"; - -void EXT_LEXER_DECL Lex(unsigned int lexer, unsigned int startPos, int length, int initStyle, -                        char *words[], WindowID window, char *props) -{ -   PropSetSimple ps; -   ps.SetMultiple(props); -   WindowAccessor wa(window, ps); - -   int nWL = 0; -   for (; words[nWL]; nWL++) ; -   WordList** wl = new WordList* [nWL + 1]; -   int i = 0; -   for (; i<nWL; i++) -   { -      wl[i] = new WordList(); -      wl[i]->Set(words[i]); -   } -   wl[i] = 0; - -   ColorizeHaskellDoc(startPos, length, initStyle, wl, wa); -   wa.Flush(); -   for (i=nWL-1;i>=0;i--) -      delete wl[i]; -   delete [] wl; -} - -void EXT_LEXER_DECL Fold (unsigned int lexer, unsigned int startPos, int length, int initStyle, -                        char *words[], WindowID window, char *props) -{ - -} - -int EXT_LEXER_DECL GetLexerCount() -{ -   return 1; -} - -void EXT_LEXER_DECL GetLexerName(unsigned int Index, char *name, int buflength) -{ -   if (buflength > 0) { -      buflength--; -      int n = strlen(LexerName); -      if (n > buflength) -         n = buflength; -      memcpy(name, LexerName, n), name[n] = '\0'; -   } -} -#endif - -LexerModule lmHaskell(SCLEX_HASKELL, ColorizeHaskellDoc, "haskell", FoldHaskellDoc, haskellWordListDesc); +LexerModule lmHaskell(SCLEX_HASKELL, LexerHaskell::LexerFactoryHaskell, "haskell", haskellWordListDesc); | 
