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

406 lines
9.9 KiB
C++

// Scintilla Lexer for EDIFACT
// @file LexEDIFACT.cxx
// Written by Iain Clarke, IMCSoft & Inobiz AB.
// EDIFACT documented here: https://www.unece.org/cefact/edifact/welcome.html
// and more readably here: https://en.wikipedia.org/wiki/EDIFACT
// This code is subject to the same license terms as the rest of the scintilla project:
// The License.txt file describes the conditions under which this software may be distributed.
//
// Header order must match order in scripts/HeaderOrder.txt
#include <cstdlib>
#include <cassert>
#include <cstring>
#include <cctype>
#include <string>
#include <string_view>
#include "ILexer.h"
#include "Scintilla.h"
#include "SciLexer.h"
#include "LexAccessor.h"
#include "LexerModule.h"
#include "DefaultLexer.h"
using namespace Scintilla;
using namespace Lexilla;
class LexerEDIFACT : public DefaultLexer
{
public:
LexerEDIFACT();
virtual ~LexerEDIFACT() {} // virtual destructor, as we inherit from ILexer
static ILexer5 *Factory() {
return new LexerEDIFACT;
}
int SCI_METHOD Version() const override
{
return lvRelease5;
}
void SCI_METHOD Release() override
{
delete this;
}
const char * SCI_METHOD PropertyNames() override
{
return "fold\nlexer.edifact.highlight.un.all";
}
int SCI_METHOD PropertyType(const char *) override
{
return SC_TYPE_BOOLEAN; // Only one property!
}
const char * SCI_METHOD DescribeProperty(const char *name) override
{
if (!strcmp(name, "fold"))
return "Whether to apply folding to document or not";
if (!strcmp(name, "lexer.edifact.highlight.un.all"))
return "Whether to apply UN* highlighting to all UN segments, or just to UNH";
return NULL;
}
Sci_Position SCI_METHOD PropertySet(const char *key, const char *val) override
{
if (!strcmp(key, "fold"))
{
m_bFold = strcmp(val, "0") ? true : false;
return 0;
}
if (!strcmp(key, "lexer.edifact.highlight.un.all")) // GetProperty
{
m_bHighlightAllUN = strcmp(val, "0") ? true : false;
return 0;
}
return -1;
}
const char * SCI_METHOD PropertyGet(const char *key) override
{
m_lastPropertyValue = "";
if (!strcmp(key, "fold"))
{
m_lastPropertyValue = m_bFold ? "1" : "0";
}
if (!strcmp(key, "lexer.edifact.highlight.un.all")) // GetProperty
{
m_lastPropertyValue = m_bHighlightAllUN ? "1" : "0";
}
return m_lastPropertyValue.c_str();
}
const char * SCI_METHOD DescribeWordListSets() override
{
return NULL;
}
Sci_Position SCI_METHOD WordListSet(int, const char *) override
{
return -1;
}
void SCI_METHOD Lex(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) override;
void SCI_METHOD Fold(Sci_PositionU startPos, Sci_Position length, int initStyle, IDocument *pAccess) override;
void * SCI_METHOD PrivateCall(int, void *) override
{
return NULL;
}
protected:
Sci_Position InitialiseFromUNA(IDocument *pAccess, Sci_PositionU MaxLength);
Sci_Position FindPreviousEnd(IDocument *pAccess, Sci_Position startPos) const;
Sci_Position ForwardPastWhitespace(IDocument *pAccess, Sci_Position startPos, Sci_Position MaxLength) const;
int DetectSegmentHeader(char SegmentHeader[3]) const;
bool m_bFold;
// property lexer.edifact.highlight.un.all
// Set to 0 to highlight only UNA segments, or 1 to highlight all UNx segments.
bool m_bHighlightAllUN;
char m_chComponent;
char m_chData;
char m_chDecimal;
char m_chRelease;
char m_chSegment;
std::string m_lastPropertyValue;
};
extern const LexerModule lmEDIFACT(SCLEX_EDIFACT, LexerEDIFACT::Factory, "edifact");
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
LexerEDIFACT::LexerEDIFACT() : DefaultLexer("edifact", SCLEX_EDIFACT)
{
m_bFold = false;
m_bHighlightAllUN = false;
m_chComponent = ':';
m_chData = '+';
m_chDecimal = '.';
m_chRelease = '?';
m_chSegment = '\'';
}
void LexerEDIFACT::Lex(Sci_PositionU startPos, Sci_Position length, int, IDocument *pAccess)
{
Sci_PositionU posFinish = startPos + length;
InitialiseFromUNA(pAccess, posFinish);
// Look backwards for a ' or a document beginning
Sci_PositionU posCurrent = FindPreviousEnd(pAccess, startPos);
// And jump past the ' if this was not the beginning of the document
if (posCurrent != 0)
posCurrent++;
// Style buffer, so we're not issuing loads of notifications
LexAccessor styler (pAccess);
pAccess->StartStyling(posCurrent);
styler.StartSegment(posCurrent);
Sci_Position posSegmentStart = -1;
while ((posCurrent < posFinish) && (posSegmentStart == -1))
{
posCurrent = ForwardPastWhitespace(pAccess, posCurrent, posFinish);
// Mark whitespace as default
styler.ColourTo(posCurrent - 1, SCE_EDI_DEFAULT);
if (posCurrent >= posFinish)
break;
// Does is start with 3 charaters? ie, UNH
char SegmentHeader[4] = { 0 };
pAccess->GetCharRange(SegmentHeader, posCurrent, 3);
int SegmentStyle = DetectSegmentHeader(SegmentHeader);
if (SegmentStyle == SCE_EDI_BADSEGMENT)
break;
if (SegmentStyle == SCE_EDI_UNA)
{
posCurrent += 9;
styler.ColourTo(posCurrent - 1, SCE_EDI_UNA); // UNA
continue;
}
posSegmentStart = posCurrent;
posCurrent += 3;
styler.ColourTo(posCurrent - 1, SegmentStyle); // UNH etc
// Colour in the rest of the segment
for (char c; posCurrent < posFinish; posCurrent++)
{
pAccess->GetCharRange(&c, posCurrent, 1);
if (c == m_chRelease) // ? escape character, check first, in case of ?'
posCurrent++;
else if (c == m_chSegment) // '
{
// Make sure the whole segment is on one line. styler won't let us go back in time, so we'll settle for marking the ' as bad.
Sci_Position lineSegmentStart = pAccess->LineFromPosition(posSegmentStart);
Sci_Position lineSegmentEnd = pAccess->LineFromPosition(posCurrent);
if (lineSegmentStart == lineSegmentEnd)
styler.ColourTo(posCurrent, SCE_EDI_SEGMENTEND);
else
styler.ColourTo(posCurrent, SCE_EDI_BADSEGMENT);
posSegmentStart = -1;
posCurrent++;
break;
}
else if (c == m_chComponent) // :
styler.ColourTo(posCurrent, SCE_EDI_SEP_COMPOSITE);
else if (c == m_chData) // +
styler.ColourTo(posCurrent, SCE_EDI_SEP_ELEMENT);
else
styler.ColourTo(posCurrent, SCE_EDI_DEFAULT);
}
}
styler.Flush();
if (posSegmentStart == -1)
return;
pAccess->StartStyling(posSegmentStart);
pAccess->SetStyleFor(posFinish - posSegmentStart, SCE_EDI_BADSEGMENT);
}
void LexerEDIFACT::Fold(Sci_PositionU startPos, Sci_Position length, int, IDocument *pAccess)
{
if (!m_bFold)
return;
Sci_PositionU endPos = startPos + length;
startPos = FindPreviousEnd(pAccess, startPos);
char c;
char SegmentHeader[4] = { 0 };
bool AwaitingSegment = true;
Sci_PositionU currLine = pAccess->LineFromPosition(startPos);
int levelCurrentStyle = SC_FOLDLEVELBASE;
if (currLine > 0)
levelCurrentStyle = pAccess->GetLevel(currLine - 1); // bottom 12 bits are level
int indentCurrent = levelCurrentStyle & SC_FOLDLEVELNUMBERMASK;
int indentNext = indentCurrent;
while (startPos < endPos)
{
pAccess->GetCharRange(&c, startPos, 1);
switch (c)
{
case '\t':
case '\r':
case ' ':
startPos++;
continue;
case '\n':
currLine = pAccess->LineFromPosition(startPos);
pAccess->SetLevel(currLine, levelCurrentStyle | indentCurrent);
startPos++;
levelCurrentStyle = SC_FOLDLEVELBASE;
indentCurrent = indentNext;
continue;
}
if (c == m_chRelease)
{
startPos += 2;
continue;
}
if (c == m_chSegment)
{
AwaitingSegment = true;
startPos++;
continue;
}
if (!AwaitingSegment)
{
startPos++;
continue;
}
// Segment!
pAccess->GetCharRange(SegmentHeader, startPos, 3);
if (SegmentHeader[0] != 'U' || SegmentHeader[1] != 'N')
{
startPos++;
continue;
}
AwaitingSegment = false;
switch (SegmentHeader[2])
{
case 'H':
case 'G':
indentNext++;
levelCurrentStyle = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG;
break;
case 'T':
case 'E':
if (indentNext > 0)
indentNext--;
break;
}
startPos += 3;
}
}
Sci_Position LexerEDIFACT::InitialiseFromUNA(IDocument *pAccess, Sci_PositionU MaxLength)
{
MaxLength -= 9; // drop 9 chars, to give us room for UNA:+.? '
Sci_PositionU startPos = 0;
startPos += ForwardPastWhitespace(pAccess, 0, MaxLength);
if (startPos < MaxLength)
{
char bufUNA[9];
pAccess->GetCharRange(bufUNA, startPos, 9);
// Check it's UNA segment
if (!memcmp(bufUNA, "UNA", 3))
{
m_chComponent = bufUNA[3];
m_chData = bufUNA[4];
m_chDecimal = bufUNA[5];
m_chRelease = bufUNA[6];
// bufUNA [7] should be space - reserved.
m_chSegment = bufUNA[8];
return 0; // success!
}
}
// We failed to find a UNA, so drop to defaults
m_chComponent = ':';
m_chData = '+';
m_chDecimal = '.';
m_chRelease = '?';
m_chSegment = '\'';
return -1;
}
Sci_Position LexerEDIFACT::ForwardPastWhitespace(IDocument *pAccess, Sci_Position startPos, Sci_Position MaxLength) const
{
char c;
while (startPos < MaxLength)
{
pAccess->GetCharRange(&c, startPos, 1);
switch (c)
{
case '\t':
case '\r':
case '\n':
case ' ':
break;
default:
return startPos;
}
startPos++;
}
return MaxLength;
}
int LexerEDIFACT::DetectSegmentHeader(char SegmentHeader[3]) const
{
if (
SegmentHeader[0] < 'A' || SegmentHeader[0] > 'Z' ||
SegmentHeader[1] < 'A' || SegmentHeader[1] > 'Z' ||
SegmentHeader[2] < 'A' || SegmentHeader[2] > 'Z')
return SCE_EDI_BADSEGMENT;
if (!memcmp(SegmentHeader, "UNA", 3))
return SCE_EDI_UNA;
if (m_bHighlightAllUN && !memcmp(SegmentHeader, "UN", 2))
return SCE_EDI_UNH;
else if (!memcmp(SegmentHeader, "UNH", 3))
return SCE_EDI_UNH;
else if (!memcmp(SegmentHeader, "UNG", 3))
return SCE_EDI_UNH;
return SCE_EDI_SEGMENTSTART;
}
// Look backwards for a ' or a document beginning
Sci_Position LexerEDIFACT::FindPreviousEnd(IDocument *pAccess, Sci_Position startPos) const
{
for (char c; startPos > 0; startPos--)
{
pAccess->GetCharRange(&c, startPos, 1);
if (c == m_chSegment)
return startPos;
}
// We didn't find a ', so just go with the beginning
return 0;
}