Christian Grasser ad79718fc8 Update to scintilla 5.5.2 & Lexilla 5.4.0
Release 5.5.2 ( https://www.scintilla.org/scintilla552.zip )

    Released 21 August 2024.
    Add SCI_SETCOPYSEPARATOR for separator between parts of a multiple selection when copied to the clipboard. Feature #1530.
    Add SCI_GETUNDOSEQUENCE to determine whether an undo sequence is active and its nesting depth.
    Add SCI_STYLESETSTRETCH to support condensed and expanded text styles.
    Add SCI_LINEINDENT and SCI_LINEDEDENT. Feature #1524.
    Fix bug on Cocoa where double-click stopped working when system had been running for a long time.
    On Cocoa implement more values of font weight and stretch.

Release 5.4.0 ( https://www.scintilla.org/lexilla540.zip )

    Released 21 August 2024.
    Inside Lexilla, LexerModule instances are now const. This will require changes to applications that modify Lexilla.cxx, which may be done to add custom lexers.
    Lexer added for TOML "toml".
    Bash: Handle backslash in heredoc delimiter. Issue #257.
    Progress: Fix lexing of nested comments. Pull request #258.
    Force lower-casing of case-insensitive keyword lists so keywords match in some lexers. Issue #259.

Close #15564
2024-08-23 02:59:58 +02:00

327 lines
10 KiB
C++

// Scintilla source code edit control
/** @file LexRebol.cxx
** Lexer for REBOL.
** Written by Pascal Hurni, inspired from LexLua by Paul Winwood & Marcos E. Wurzius & Philippe Lhoste
**
** History:
** 2005-04-07 First release.
** 2005-04-10 Closing parens and brackets go now in default style
** String and comment nesting should be more safe
**/
// Copyright 2005 by Pascal Hurni <pascal_hurni@fastmail.fm>
// 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 <string>
#include <string_view>
#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"
using namespace Lexilla;
static inline bool IsAWordChar(const int ch) {
return (isalnum(ch) || ch == '?' || ch == '!' || ch == '.' || ch == '\'' || ch == '+' || ch == '-' || ch == '*' || ch == '&' || ch == '|' || ch == '=' || ch == '_' || ch == '~');
}
static inline bool IsAWordStart(const int ch, const int ch2) {
return ((ch == '+' || ch == '-' || ch == '.') && !isdigit(ch2)) ||
(isalpha(ch) || ch == '?' || ch == '!' || ch == '\'' || ch == '*' || ch == '&' || ch == '|' || ch == '=' || ch == '_' || ch == '~');
}
static inline bool IsAnOperator(const int ch, const int ch2, const int ch3) {
// One char operators
if (IsASpaceOrTab(ch2)) {
return ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '<' || ch == '>' || ch == '=' || ch == '?';
}
// Two char operators
if (IsASpaceOrTab(ch3)) {
return (ch == '*' && ch2 == '*') ||
(ch == '/' && ch2 == '/') ||
(ch == '<' && (ch2 == '=' || ch2 == '>')) ||
(ch == '>' && ch2 == '=') ||
(ch == '=' && (ch2 == '=' || ch2 == '?')) ||
(ch == '?' && ch2 == '?');
}
return false;
}
static inline bool IsBinaryStart(const int ch, const int ch2, const int ch3, const int ch4) {
return (ch == '#' && ch2 == '{') ||
(IsADigit(ch) && ch2 == '#' && ch3 == '{' ) ||
(IsADigit(ch) && IsADigit(ch2) && ch3 == '#' && ch4 == '{' );
}
static void ColouriseRebolDoc(Sci_PositionU startPos, Sci_Position length, int initStyle, WordList *keywordlists[], Accessor &styler) {
WordList &keywords = *keywordlists[0];
WordList &keywords2 = *keywordlists[1];
WordList &keywords3 = *keywordlists[2];
WordList &keywords4 = *keywordlists[3];
WordList &keywords5 = *keywordlists[4];
WordList &keywords6 = *keywordlists[5];
WordList &keywords7 = *keywordlists[6];
WordList &keywords8 = *keywordlists[7];
Sci_Position currentLine = styler.GetLine(startPos);
// Initialize the braced string {.. { ... } ..} nesting level, if we are inside such a string.
int stringLevel = 0;
if (initStyle == SCE_REBOL_BRACEDSTRING || initStyle == SCE_REBOL_COMMENTBLOCK) {
stringLevel = styler.GetLineState(currentLine - 1);
}
bool blockComment = initStyle == SCE_REBOL_COMMENTBLOCK;
int dotCount = 0;
// Do not leak onto next line
if (initStyle == SCE_REBOL_COMMENTLINE) {
initStyle = SCE_REBOL_DEFAULT;
}
StyleContext sc(startPos, length, initStyle, styler);
if (startPos == 0) {
sc.SetState(SCE_REBOL_PREFACE);
}
for (; sc.More(); sc.Forward()) {
//--- What to do at line end ?
if (sc.atLineEnd) {
// Can be either inside a {} string or simply at eol
if (sc.state != SCE_REBOL_BRACEDSTRING && sc.state != SCE_REBOL_COMMENTBLOCK &&
sc.state != SCE_REBOL_BINARY && sc.state != SCE_REBOL_PREFACE)
sc.SetState(SCE_REBOL_DEFAULT);
// Update the line state, so it can be seen by next line
currentLine = styler.GetLine(sc.currentPos);
switch (sc.state) {
case SCE_REBOL_BRACEDSTRING:
case SCE_REBOL_COMMENTBLOCK:
// Inside a braced string, we set the line state
styler.SetLineState(currentLine, stringLevel);
break;
default:
// Reset the line state
styler.SetLineState(currentLine, 0);
break;
}
// continue with next char
continue;
}
//--- What to do on white-space ?
if (IsASpaceOrTab(sc.ch))
{
// Return to default if any of these states
if (sc.state == SCE_REBOL_OPERATOR || sc.state == SCE_REBOL_CHARACTER ||
sc.state == SCE_REBOL_NUMBER || sc.state == SCE_REBOL_PAIR ||
sc.state == SCE_REBOL_TUPLE || sc.state == SCE_REBOL_FILE ||
sc.state == SCE_REBOL_DATE || sc.state == SCE_REBOL_TIME ||
sc.state == SCE_REBOL_MONEY || sc.state == SCE_REBOL_ISSUE ||
sc.state == SCE_REBOL_URL || sc.state == SCE_REBOL_EMAIL) {
sc.SetState(SCE_REBOL_DEFAULT);
}
}
//--- Specialize state ?
// URL, Email look like identifier
if (sc.state == SCE_REBOL_IDENTIFIER)
{
if (sc.ch == ':' && !IsASpace(sc.chNext)) {
sc.ChangeState(SCE_REBOL_URL);
} else if (sc.ch == '@') {
sc.ChangeState(SCE_REBOL_EMAIL);
} else if (sc.ch == '$') {
sc.ChangeState(SCE_REBOL_MONEY);
}
}
// Words look like identifiers
if (sc.state == SCE_REBOL_IDENTIFIER || (sc.state >= SCE_REBOL_WORD && sc.state <= SCE_REBOL_WORD8)) {
// Keywords ?
if (!IsAWordChar(sc.ch) || sc.Match('/')) {
char s[100];
sc.GetCurrentLowered(s, sizeof(s));
blockComment = strcmp(s, "comment") == 0;
if (keywords8.InList(s)) {
sc.ChangeState(SCE_REBOL_WORD8);
} else if (keywords7.InList(s)) {
sc.ChangeState(SCE_REBOL_WORD7);
} else if (keywords6.InList(s)) {
sc.ChangeState(SCE_REBOL_WORD6);
} else if (keywords5.InList(s)) {
sc.ChangeState(SCE_REBOL_WORD5);
} else if (keywords4.InList(s)) {
sc.ChangeState(SCE_REBOL_WORD4);
} else if (keywords3.InList(s)) {
sc.ChangeState(SCE_REBOL_WORD3);
} else if (keywords2.InList(s)) {
sc.ChangeState(SCE_REBOL_WORD2);
} else if (keywords.InList(s)) {
sc.ChangeState(SCE_REBOL_WORD);
}
// Keep same style if there are refinements
if (!sc.Match('/')) {
sc.SetState(SCE_REBOL_DEFAULT);
}
}
// special numbers
} else if (sc.state == SCE_REBOL_NUMBER) {
switch (sc.ch) {
case 'x': sc.ChangeState(SCE_REBOL_PAIR);
break;
case ':': sc.ChangeState(SCE_REBOL_TIME);
break;
case '-':
case '/': sc.ChangeState(SCE_REBOL_DATE);
break;
case '.': if (++dotCount >= 2) sc.ChangeState(SCE_REBOL_TUPLE);
break;
}
}
//--- Determine if the current state should terminate
if (sc.state == SCE_REBOL_QUOTEDSTRING || sc.state == SCE_REBOL_CHARACTER) {
if (sc.ch == '^' && sc.chNext == '\"') {
sc.Forward();
} else if (sc.ch == '\"') {
sc.ForwardSetState(SCE_REBOL_DEFAULT);
}
} else if (sc.state == SCE_REBOL_BRACEDSTRING || sc.state == SCE_REBOL_COMMENTBLOCK) {
if (sc.ch == '}') {
if (--stringLevel == 0) {
sc.ForwardSetState(SCE_REBOL_DEFAULT);
}
} else if (sc.ch == '{') {
stringLevel++;
}
} else if (sc.state == SCE_REBOL_BINARY) {
if (sc.ch == '}') {
sc.ForwardSetState(SCE_REBOL_DEFAULT);
}
} else if (sc.state == SCE_REBOL_TAG) {
if (sc.ch == '>') {
sc.ForwardSetState(SCE_REBOL_DEFAULT);
}
} else if (sc.state == SCE_REBOL_PREFACE) {
if (sc.MatchIgnoreCase("rebol"))
{
int i;
for (i=5; IsASpaceOrTab(styler.SafeGetCharAt(sc.currentPos+i, 0)); i++);
if (sc.GetRelative(i) == '[')
sc.SetState(SCE_REBOL_DEFAULT);
}
}
//--- Parens and bracket changes to default style when the current is a number
if (sc.state == SCE_REBOL_NUMBER || sc.state == SCE_REBOL_PAIR || sc.state == SCE_REBOL_TUPLE ||
sc.state == SCE_REBOL_MONEY || sc.state == SCE_REBOL_ISSUE || sc.state == SCE_REBOL_EMAIL ||
sc.state == SCE_REBOL_URL || sc.state == SCE_REBOL_DATE || sc.state == SCE_REBOL_TIME) {
if (sc.ch == '(' || sc.ch == '[' || sc.ch == ')' || sc.ch == ']') {
sc.SetState(SCE_REBOL_DEFAULT);
}
}
//--- Determine if a new state should be entered.
if (sc.state == SCE_REBOL_DEFAULT) {
if (IsAnOperator(sc.ch, sc.chNext, sc.GetRelative(2))) {
sc.SetState(SCE_REBOL_OPERATOR);
} else if (IsBinaryStart(sc.ch, sc.chNext, sc.GetRelative(2), sc.GetRelative(3))) {
sc.SetState(SCE_REBOL_BINARY);
} else if (IsAWordStart(sc.ch, sc.chNext)) {
sc.SetState(SCE_REBOL_IDENTIFIER);
} else if (IsADigit(sc.ch) || sc.ch == '+' || sc.ch == '-' || /*Decimal*/ sc.ch == '.' || sc.ch == ',') {
dotCount = 0;
sc.SetState(SCE_REBOL_NUMBER);
} else if (sc.ch == '\"') {
sc.SetState(SCE_REBOL_QUOTEDSTRING);
} else if (sc.ch == '{') {
sc.SetState(blockComment ? SCE_REBOL_COMMENTBLOCK : SCE_REBOL_BRACEDSTRING);
++stringLevel;
} else if (sc.ch == ';') {
sc.SetState(SCE_REBOL_COMMENTLINE);
} else if (sc.ch == '$') {
sc.SetState(SCE_REBOL_MONEY);
} else if (sc.ch == '%') {
sc.SetState(SCE_REBOL_FILE);
} else if (sc.ch == '<') {
sc.SetState(SCE_REBOL_TAG);
} else if (sc.ch == '#' && sc.chNext == '"') {
sc.SetState(SCE_REBOL_CHARACTER);
sc.Forward();
} else if (sc.ch == '#' && sc.chNext != '"' && sc.chNext != '{' ) {
sc.SetState(SCE_REBOL_ISSUE);
}
}
}
sc.Complete();
}
static void FoldRebolDoc(Sci_PositionU startPos, Sci_Position length, int /* initStyle */, WordList *[],
Accessor &styler) {
Sci_PositionU lengthDoc = startPos + length;
int visibleChars = 0;
Sci_Position lineCurrent = styler.GetLine(startPos);
int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
int levelCurrent = levelPrev;
char chNext = styler[startPos];
int styleNext = styler.StyleAt(startPos);
for (Sci_PositionU i = startPos; i < lengthDoc; i++) {
char ch = chNext;
chNext = styler.SafeGetCharAt(i + 1);
int style = styleNext;
styleNext = styler.StyleAt(i + 1);
bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
if (style == SCE_REBOL_DEFAULT) {
if (ch == '[') {
levelCurrent++;
} else if (ch == ']') {
levelCurrent--;
}
}
if (atEOL) {
int lev = levelPrev;
if (visibleChars == 0)
lev |= SC_FOLDLEVELWHITEFLAG;
if ((levelCurrent > levelPrev) && (visibleChars > 0))
lev |= SC_FOLDLEVELHEADERFLAG;
if (lev != styler.LevelAt(lineCurrent)) {
styler.SetLevel(lineCurrent, lev);
}
lineCurrent++;
levelPrev = levelCurrent;
visibleChars = 0;
}
if (!isspacechar(ch))
visibleChars++;
}
// Fill in the real level of the next line, keeping the current flags as they will be filled in later
int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
styler.SetLevel(lineCurrent, levelPrev | flagsNext);
}
static const char * const rebolWordListDesc[] = {
"Keywords",
0
};
extern const LexerModule lmREBOL(SCLEX_REBOL, ColouriseRebolDoc, "rebol", FoldRebolDoc, rebolWordListDesc);