123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- // Scintilla source code edit control
- /** @file LexKVIrc.cxx
- ** Lexer for KVIrc script.
- **/
- // Copyright 2013 by OmegaPhil <OmegaPhil+scintilla@gmail.com>, based in
- // part from LexPython Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
- // and LexCmake Copyright 2007 by Cristian Adam <cristian [dot] adam [at] gmx [dot] net>
- // 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 "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
- /* KVIrc Script syntactic rules: http://www.kvirc.net/doc/doc_syntactic_rules.html */
- /* Utility functions */
- static inline bool IsAWordChar(int ch) {
- /* Keyword list includes modules, i.e. words including '.', and
- * alias namespaces include ':' */
- return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '.'
- || ch == ':');
- }
- static inline bool IsAWordStart(int ch) {
- /* Functions (start with '$') are treated separately to keywords */
- return (ch < 0x80) && (isalnum(ch) || ch == '_' );
- }
- /* Interface function called by Scintilla to request some text to be
- syntax highlighted */
- static void ColouriseKVIrcDoc(Sci_PositionU startPos, Sci_Position length,
- int initStyle, WordList *keywordlists[],
- Accessor &styler)
- {
- /* Fetching style context */
- StyleContext sc(startPos, length, initStyle, styler);
- /* Accessing keywords and function-marking keywords */
- WordList &keywords = *keywordlists[0];
- WordList &functionKeywords = *keywordlists[1];
- /* Looping for all characters - only automatically moving forward
- * when asked for (transitions leaving strings and keywords do this
- * already) */
- bool next = true;
- for( ; sc.More(); next ? sc.Forward() : (void)0 )
- {
- /* Resetting next */
- next = true;
- /* Dealing with different states */
- switch (sc.state)
- {
- case SCE_KVIRC_DEFAULT:
- /* Detecting single-line comments
- * Unfortunately KVIrc script allows raw '#<channel
- * name>' to be used, and appending # to an array returns
- * its length...
- * Going for a compromise where single line comments not
- * starting on a newline are allowed in all cases except
- * when they are preceeded with an opening bracket or comma
- * (this will probably be the most common style a valid
- * string-less channel name will be used with), with the
- * array length case included
- */
- if (
- (sc.ch == '#' && sc.atLineStart) ||
- (sc.ch == '#' && (
- sc.chPrev != '(' && sc.chPrev != ',' &&
- sc.chPrev != ']')
- )
- )
- {
- sc.SetState(SCE_KVIRC_COMMENT);
- break;
- }
- /* Detecting multi-line comments */
- if (sc.Match('/', '*'))
- {
- sc.SetState(SCE_KVIRC_COMMENTBLOCK);
- break;
- }
- /* Detecting strings */
- if (sc.ch == '"')
- {
- sc.SetState(SCE_KVIRC_STRING);
- break;
- }
- /* Detecting functions */
- if (sc.ch == '$')
- {
- sc.SetState(SCE_KVIRC_FUNCTION);
- break;
- }
- /* Detecting variables */
- if (sc.ch == '%')
- {
- sc.SetState(SCE_KVIRC_VARIABLE);
- break;
- }
- /* Detecting numbers - isdigit is unsafe as it does not
- * validate, use CharacterSet.h functions */
- if (IsADigit(sc.ch))
- {
- sc.SetState(SCE_KVIRC_NUMBER);
- break;
- }
- /* Detecting words */
- if (IsAWordStart(sc.ch) && IsAWordChar(sc.chNext))
- {
- sc.SetState(SCE_KVIRC_WORD);
- sc.Forward();
- break;
- }
- /* Detecting operators */
- if (isoperator(sc.ch))
- {
- sc.SetState(SCE_KVIRC_OPERATOR);
- break;
- }
- break;
- case SCE_KVIRC_COMMENT:
- /* Breaking out of single line comment when a newline
- * is introduced */
- if (sc.ch == '\r' || sc.ch == '\n')
- {
- sc.SetState(SCE_KVIRC_DEFAULT);
- break;
- }
- break;
- case SCE_KVIRC_COMMENTBLOCK:
- /* Detecting end of multi-line comment */
- if (sc.Match('*', '/'))
- {
- // Moving the current position forward two characters
- // so that '*/' is included in the comment
- sc.Forward(2);
- sc.SetState(SCE_KVIRC_DEFAULT);
- /* Comment has been exited and the current position
- * moved forward, yet the new current character
- * has yet to be defined - loop without moving
- * forward again */
- next = false;
- break;
- }
- break;
- case SCE_KVIRC_STRING:
- /* Detecting end of string - closing speechmarks */
- if (sc.ch == '"')
- {
- /* Allowing escaped speechmarks to pass */
- if (sc.chPrev == '\\')
- break;
- /* Moving the current position forward to capture the
- * terminating speechmarks, and ending string */
- sc.ForwardSetState(SCE_KVIRC_DEFAULT);
- /* String has been exited and the current position
- * moved forward, yet the new current character
- * has yet to be defined - loop without moving
- * forward again */
- next = false;
- break;
- }
- /* Functions and variables are now highlighted in strings
- * Detecting functions */
- if (sc.ch == '$')
- {
- /* Allowing escaped functions to pass */
- if (sc.chPrev == '\\')
- break;
- sc.SetState(SCE_KVIRC_STRING_FUNCTION);
- break;
- }
- /* Detecting variables */
- if (sc.ch == '%')
- {
- /* Allowing escaped variables to pass */
- if (sc.chPrev == '\\')
- break;
- sc.SetState(SCE_KVIRC_STRING_VARIABLE);
- break;
- }
- /* Breaking out of a string when a newline is introduced */
- if (sc.ch == '\r' || sc.ch == '\n')
- {
- /* Allowing escaped newlines */
- if (sc.chPrev == '\\')
- break;
- sc.SetState(SCE_KVIRC_DEFAULT);
- break;
- }
- break;
- case SCE_KVIRC_FUNCTION:
- case SCE_KVIRC_VARIABLE:
- /* Detecting the end of a function/variable (word) */
- if (!IsAWordChar(sc.ch))
- {
- sc.SetState(SCE_KVIRC_DEFAULT);
- /* Word has been exited yet the current character
- * has yet to be defined - loop without moving
- * forward again */
- next = false;
- break;
- }
- break;
- case SCE_KVIRC_STRING_FUNCTION:
- case SCE_KVIRC_STRING_VARIABLE:
- /* A function or variable in a string
- * Detecting the end of a function/variable (word) */
- if (!IsAWordChar(sc.ch))
- {
- sc.SetState(SCE_KVIRC_STRING);
- /* Word has been exited yet the current character
- * has yet to be defined - loop without moving
- * forward again */
- next = false;
- break;
- }
- break;
- case SCE_KVIRC_NUMBER:
- /* Detecting the end of a number */
- if (!IsADigit(sc.ch))
- {
- sc.SetState(SCE_KVIRC_DEFAULT);
- /* Number has been exited yet the current character
- * has yet to be defined - loop without moving
- * forward */
- next = false;
- break;
- }
- break;
- case SCE_KVIRC_OPERATOR:
- /* Because '%' is an operator but is also the marker for
- * a variable, I need to always treat operators as single
- * character strings and therefore redo their detection
- * after every character */
- sc.SetState(SCE_KVIRC_DEFAULT);
- /* Operator has been exited yet the current character
- * has yet to be defined - loop without moving
- * forward */
- next = false;
- break;
- case SCE_KVIRC_WORD:
- /* Detecting the end of a word */
- if (!IsAWordChar(sc.ch))
- {
- /* Checking if the word was actually a keyword -
- * fetching the current word, NULL-terminated like
- * the keyword list */
- char s[100];
- Sci_Position wordLen = sc.currentPos - styler.GetStartSegment();
- if (wordLen > 99)
- wordLen = 99; /* Include '\0' in buffer */
- Sci_Position i;
- for( i = 0; i < wordLen; ++i )
- {
- s[i] = styler.SafeGetCharAt( styler.GetStartSegment() + i );
- }
- s[wordLen] = '\0';
- /* Actually detecting keywords and fixing the state */
- if (keywords.InList(s))
- {
- /* The SetState call actually commits the
- * previous keyword state */
- sc.ChangeState(SCE_KVIRC_KEYWORD);
- }
- else if (functionKeywords.InList(s))
- {
- // Detecting function keywords and fixing the state
- sc.ChangeState(SCE_KVIRC_FUNCTION_KEYWORD);
- }
- /* Transitioning to default and committing the previous
- * word state */
- sc.SetState(SCE_KVIRC_DEFAULT);
- /* Word has been exited yet the current character
- * has yet to be defined - loop without moving
- * forward again */
- next = false;
- break;
- }
- break;
- }
- }
- /* Indicating processing is complete */
- sc.Complete();
- }
- static void FoldKVIrcDoc(Sci_PositionU startPos, Sci_Position length, int /*initStyle - unused*/,
- WordList *[], Accessor &styler)
- {
- /* Based on CMake's folder */
- /* Exiting if folding isnt enabled */
- if ( styler.GetPropertyInt("fold") == 0 )
- return;
- /* Obtaining current line number*/
- Sci_Position currentLine = styler.GetLine(startPos);
- /* Obtaining starting character - indentation is done on a line basis,
- * not character */
- Sci_PositionU safeStartPos = styler.LineStart( currentLine );
- /* Initialising current level - this is defined as indentation level
- * in the low 12 bits, with flag bits in the upper four bits.
- * It looks like two indentation states are maintained in the returned
- * 32bit value - 'nextLevel' in the most-significant bits, 'currentLevel'
- * in the least-significant bits. Since the next level is the most
- * up to date, this must refer to the current state of indentation.
- * So the code bitshifts the old current level out of existence to
- * get at the actual current state of indentation
- * Based on the LexerCPP.cxx line 958 comment */
- int currentLevel = SC_FOLDLEVELBASE;
- if (currentLine > 0)
- currentLevel = styler.LevelAt(currentLine - 1) >> 16;
- int nextLevel = currentLevel;
- // Looping for characters in range
- for (Sci_PositionU i = safeStartPos; i < startPos + length; ++i)
- {
- /* Folding occurs after syntax highlighting, meaning Scintilla
- * already knows where the comments are
- * Fetching the current state */
- int state = styler.StyleAt(i) & 31;
- switch( styler.SafeGetCharAt(i) )
- {
- case '{':
- /* Indenting only when the braces are not contained in
- * a comment */
- if (state != SCE_KVIRC_COMMENT &&
- state != SCE_KVIRC_COMMENTBLOCK)
- ++nextLevel;
- break;
- case '}':
- /* Outdenting only when the braces are not contained in
- * a comment */
- if (state != SCE_KVIRC_COMMENT &&
- state != SCE_KVIRC_COMMENTBLOCK)
- --nextLevel;
- break;
- case '\n':
- case '\r':
- /* Preparing indentation information to return - combining
- * current and next level data */
- int lev = currentLevel | nextLevel << 16;
- /* If the next level increases the indent level, mark the
- * current line as a fold point - current level data is
- * in the least significant bits */
- if (nextLevel > currentLevel )
- lev |= SC_FOLDLEVELHEADERFLAG;
- /* Updating indentation level if needed */
- if (lev != styler.LevelAt(currentLine))
- styler.SetLevel(currentLine, lev);
- /* Updating variables */
- ++currentLine;
- currentLevel = nextLevel;
- /* Dealing with problematic Windows newlines -
- * incrementing to avoid the extra newline breaking the
- * fold point */
- if (styler.SafeGetCharAt(i) == '\r' &&
- styler.SafeGetCharAt(i + 1) == '\n')
- ++i;
- break;
- }
- }
- /* At this point the data has ended, so presumably the end of the line?
- * Preparing indentation information to return - combining current
- * and next level data */
- int lev = currentLevel | nextLevel << 16;
- /* If the next level increases the indent level, mark the current
- * line as a fold point - current level data is in the least
- * significant bits */
- if (nextLevel > currentLevel )
- lev |= SC_FOLDLEVELHEADERFLAG;
- /* Updating indentation level if needed */
- if (lev != styler.LevelAt(currentLine))
- styler.SetLevel(currentLine, lev);
- }
- /* Registering wordlists */
- static const char *const kvircWordListDesc[] = {
- "primary",
- "function_keywords",
- 0
- };
- /* Registering functions and wordlists */
- LexerModule lmKVIrc(SCLEX_KVIRC, ColouriseKVIrcDoc, "kvirc", FoldKVIrcDoc,
- kvircWordListDesc);
|