fix build under newer KConfig by bump cmake min version

This commit is contained in:
2024-09-04 19:01:58 +08:00
parent 727a2ec214
commit 9fb3681e3a
1022 changed files with 4414 additions and 1375 deletions

View File

@ -0,0 +1,76 @@
// Scintilla source code edit control
/** @file Accessor.cxx
** Interfaces between Scintilla and lexers.
**/
// Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstdlib>
#include <cassert>
#include <string>
#include <string_view>
#include "ILexer.h"
#include "Scintilla.h"
#include "SciLexer.h"
#include "PropSetSimple.h"
#include "WordList.h"
#include "LexAccessor.h"
#include "Accessor.h"
using namespace Lexilla;
Accessor::Accessor(Scintilla::IDocument *pAccess_, PropSetSimple *pprops_) : LexAccessor(pAccess_), pprops(pprops_) {
}
int Accessor::GetPropertyInt(std::string_view key, int defaultValue) const {
return pprops->GetInt(key, defaultValue);
}
int Accessor::IndentAmount(Sci_Position line, int *flags, PFNIsCommentLeader pfnIsCommentLeader) {
const Sci_Position end = Length();
int spaceFlags = 0;
// Determines the indentation level of the current line and also checks for consistent
// indentation compared to the previous line.
// Indentation is judged consistent when the indentation whitespace of each line lines
// the same or the indentation of one line is a prefix of the other.
Sci_Position pos = LineStart(line);
char ch = (*this)[pos];
int indent = 0;
bool inPrevPrefix = line > 0;
Sci_Position posPrev = inPrevPrefix ? LineStart(line-1) : 0;
while ((ch == ' ' || ch == '\t') && (pos < end)) {
if (inPrevPrefix) {
const char chPrev = (*this)[posPrev++];
if (chPrev == ' ' || chPrev == '\t') {
if (chPrev != ch)
spaceFlags |= wsInconsistent;
} else {
inPrevPrefix = false;
}
}
if (ch == ' ') {
spaceFlags |= wsSpace;
indent++;
} else { // Tab
spaceFlags |= wsTab;
if (spaceFlags & wsSpace)
spaceFlags |= wsSpaceTab;
indent = (indent / 8 + 1) * 8;
}
ch = (*this)[++pos];
}
*flags = spaceFlags;
indent += SC_FOLDLEVELBASE;
// if completely empty line or the start of a comment...
if ((LineStart(line) == Length()) || (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') ||
(pfnIsCommentLeader && (*pfnIsCommentLeader)(*this, pos, end-pos)))
return indent | SC_FOLDLEVELWHITEFLAG;
else
return indent;
}

View File

@ -0,0 +1,31 @@
// Scintilla source code edit control
/** @file Accessor.h
** Interfaces between Scintilla and lexers.
**/
// Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef ACCESSOR_H
#define ACCESSOR_H
namespace Lexilla {
enum { wsSpace=1, wsTab=2, wsSpaceTab=4, wsInconsistent=8 };
class Accessor;
class WordList;
class PropSetSimple;
typedef bool (*PFNIsCommentLeader)(Accessor &styler, Sci_Position pos, Sci_Position len);
class Accessor : public LexAccessor {
public:
PropSetSimple *pprops;
Accessor(Scintilla::IDocument *pAccess_, PropSetSimple *pprops_);
int GetPropertyInt(std::string_view key, int defaultValue=0) const;
int IndentAmount(Sci_Position line, int *flags, PFNIsCommentLeader pfnIsCommentLeader = nullptr);
};
}
#endif

View File

@ -0,0 +1,73 @@
// Scintilla source code edit control
/** @file CatalogueModules.h
** Lexer infrastructure.
** Contains a list of LexerModules which can be searched to find a module appropriate for a
** particular language.
**/
// Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef CATALOGUEMODULES_H
#define CATALOGUEMODULES_H
namespace Lexilla {
class CatalogueModules {
std::vector<const LexerModule *> lexerCatalogue;
public:
const LexerModule *Find(int language) const noexcept {
for (const LexerModule *lm : lexerCatalogue) {
if (lm->GetLanguage() == language) {
return lm;
}
}
return nullptr;
}
const LexerModule *Find(const char *languageName) const noexcept {
if (languageName) {
for (const LexerModule *lm : lexerCatalogue) {
if (lm->languageName && (0 == strcmp(lm->languageName, languageName))) {
return lm;
}
}
}
return nullptr;
}
void AddLexerModule(const LexerModule *plm) {
lexerCatalogue.push_back(plm);
}
void AddLexerModules(std::initializer_list<const LexerModule *> modules) {
lexerCatalogue.insert(lexerCatalogue.end(), modules);
}
size_t Count() const noexcept {
return lexerCatalogue.size();
}
const char *Name(size_t index) const noexcept {
if (index < lexerCatalogue.size()) {
return lexerCatalogue[index]->languageName;
}
return "";
}
LexerFactoryFunction Factory(size_t index) const noexcept {
// Works for object lexers but not for function lexers
return lexerCatalogue[index]->fnFactory;
}
Scintilla::ILexer5 *Create(size_t index) const {
const LexerModule *plm = lexerCatalogue[index];
if (!plm) {
return nullptr;
}
return plm->Create();
}
};
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,50 @@
// Scintilla source code edit control
/** @file CharacterCategory.h
** Returns the Unicode general category of a character.
**/
// Copyright 2013 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef CHARACTERCATEGORY_H
#define CHARACTERCATEGORY_H
namespace Lexilla {
enum CharacterCategory {
ccLu, ccLl, ccLt, ccLm, ccLo,
ccMn, ccMc, ccMe,
ccNd, ccNl, ccNo,
ccPc, ccPd, ccPs, ccPe, ccPi, ccPf, ccPo,
ccSm, ccSc, ccSk, ccSo,
ccZs, ccZl, ccZp,
ccCc, ccCf, ccCs, ccCo, ccCn
};
CharacterCategory CategoriseCharacter(int character) noexcept;
// Common definitions of allowable characters in identifiers from UAX #31.
bool IsIdStart(int character) noexcept;
bool IsIdContinue(int character) noexcept;
bool IsXidStart(int character) noexcept;
bool IsXidContinue(int character) noexcept;
class CharacterCategoryMap {
private:
std::vector<unsigned char> dense;
public:
CharacterCategoryMap();
CharacterCategory CategoryFor(int character) const noexcept {
if (static_cast<size_t>(character) < dense.size()) {
return static_cast<CharacterCategory>(dense[character]);
} else {
// binary search through ranges
return CategoriseCharacter(character);
}
}
int Size() const noexcept;
void Optimize(int countCharacters);
};
}
#endif

View File

@ -0,0 +1,66 @@
// Scintilla source code edit control
/** @file CharacterSet.cxx
** Simple case functions for ASCII.
** Lexer infrastructure.
**/
// Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstdlib>
#include <cassert>
#include <string_view>
#include "CharacterSet.h"
using namespace Lexilla;
namespace Lexilla {
int CompareCaseInsensitive(const char *a, const char *b) noexcept {
while (*a && *b) {
if (*a != *b) {
const char upperA = MakeUpperCase(*a);
const char upperB = MakeUpperCase(*b);
if (upperA != upperB)
return upperA - upperB;
}
a++;
b++;
}
// Either *a or *b is nul
return *a - *b;
}
bool EqualCaseInsensitive(std::string_view a, std::string_view b) noexcept {
if (a.length() != b.length()) {
return false;
}
for (size_t i = 0; i < a.length(); i++) {
if (MakeUpperCase(a[i]) != MakeUpperCase(b[i])) {
return false;
}
}
return true;
}
int CompareNCaseInsensitive(const char *a, const char *b, size_t len) noexcept {
while (*a && *b && len) {
if (*a != *b) {
const char upperA = MakeUpperCase(*a);
const char upperB = MakeUpperCase(*b);
if (upperA != upperB)
return upperA - upperB;
}
a++;
b++;
len--;
}
if (len == 0)
return 0;
else
// Either *a or *b is nul
return *a - *b;
}
}

View File

@ -0,0 +1,195 @@
// Scintilla source code edit control
/** @file CharacterSet.h
** Encapsulates a set of characters. Used to test if a character is within a set.
**/
// Copyright 2007 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef CHARACTERSET_H
#define CHARACTERSET_H
namespace Lexilla {
template<int N>
class CharacterSetArray {
unsigned char bset[(N-1)/8 + 1] = {};
bool valueAfter = false;
public:
enum setBase {
setNone=0,
setLower=1,
setUpper=2,
setDigits=4,
setAlpha=setLower|setUpper,
setAlphaNum=setAlpha|setDigits
};
CharacterSetArray(setBase base=setNone, const char *initialSet="", bool valueAfter_=false) noexcept {
valueAfter = valueAfter_;
AddString(initialSet);
if (base & setLower)
AddString("abcdefghijklmnopqrstuvwxyz");
if (base & setUpper)
AddString("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
if (base & setDigits)
AddString("0123456789");
}
CharacterSetArray(const char *initialSet, bool valueAfter_=false) noexcept :
CharacterSetArray(setNone, initialSet, valueAfter_) {
}
// For compatibility with previous version but should not be used in new code.
CharacterSetArray(setBase base, const char *initialSet, [[maybe_unused]]int size_, bool valueAfter_=false) noexcept :
CharacterSetArray(base, initialSet, valueAfter_) {
assert(size_ == N);
}
void Add(int val) noexcept {
assert(val >= 0);
assert(val < N);
bset[val >> 3] |= 1 << (val & 7);
}
void AddString(const char *setToAdd) noexcept {
for (const char *cp=setToAdd; *cp; cp++) {
const unsigned char uch = *cp;
assert(uch < N);
Add(uch);
}
}
bool Contains(int val) const noexcept {
assert(val >= 0);
if (val < 0) return false;
if (val >= N) return valueAfter;
return bset[val >> 3] & (1 << (val & 7));
}
bool Contains(char ch) const noexcept {
// Overload char as char may be signed
const unsigned char uch = ch;
return Contains(uch);
}
};
using CharacterSet = CharacterSetArray<0x80>;
// Functions for classifying characters
template <typename T, typename... Args>
constexpr bool AnyOf(T t, Args... args) noexcept {
#if defined(__clang__)
static_assert(__is_integral(T) || __is_enum(T));
#endif
return ((t == args) || ...);
}
// prevent pointer without <type_traits>
template <typename T, typename... Args>
constexpr void AnyOf([[maybe_unused]] T *t, [[maybe_unused]] Args... args) noexcept {}
template <typename T, typename... Args>
constexpr void AnyOf([[maybe_unused]] const T *t, [[maybe_unused]] Args... args) noexcept {}
constexpr bool IsASpace(int ch) noexcept {
return (ch == ' ') || ((ch >= 0x09) && (ch <= 0x0d));
}
constexpr bool IsASpaceOrTab(int ch) noexcept {
return (ch == ' ') || (ch == '\t');
}
constexpr bool IsADigit(int ch) noexcept {
return (ch >= '0') && (ch <= '9');
}
constexpr bool IsAHeXDigit(int ch) noexcept {
return (ch >= '0' && ch <= '9')
|| (ch >= 'A' && ch <= 'F')
|| (ch >= 'a' && ch <= 'f');
}
constexpr bool IsAnOctalDigit(int ch) noexcept {
return ch >= '0' && ch <= '7';
}
constexpr bool IsADigit(int ch, int base) noexcept {
if (base <= 10) {
return (ch >= '0') && (ch < '0' + base);
} else {
return ((ch >= '0') && (ch <= '9')) ||
((ch >= 'A') && (ch < 'A' + base - 10)) ||
((ch >= 'a') && (ch < 'a' + base - 10));
}
}
constexpr bool IsASCII(int ch) noexcept {
return (ch >= 0) && (ch < 0x80);
}
constexpr bool IsLowerCase(int ch) noexcept {
return (ch >= 'a') && (ch <= 'z');
}
constexpr bool IsUpperCase(int ch) noexcept {
return (ch >= 'A') && (ch <= 'Z');
}
constexpr bool IsUpperOrLowerCase(int ch) noexcept {
return IsUpperCase(ch) || IsLowerCase(ch);
}
constexpr bool IsAlphaNumeric(int ch) noexcept {
return
((ch >= '0') && (ch <= '9')) ||
((ch >= 'a') && (ch <= 'z')) ||
((ch >= 'A') && (ch <= 'Z'));
}
/**
* Check if a character is a space.
* This is ASCII specific but is safe with chars >= 0x80.
*/
constexpr bool isspacechar(int ch) noexcept {
return (ch == ' ') || ((ch >= 0x09) && (ch <= 0x0d));
}
constexpr bool iswordchar(int ch) noexcept {
return IsAlphaNumeric(ch) || ch == '.' || ch == '_';
}
constexpr bool iswordstart(int ch) noexcept {
return IsAlphaNumeric(ch) || ch == '_';
}
constexpr bool isoperator(int ch) noexcept {
if (IsAlphaNumeric(ch))
return false;
if (ch == '%' || ch == '^' || ch == '&' || ch == '*' ||
ch == '(' || ch == ')' || ch == '-' || ch == '+' ||
ch == '=' || ch == '|' || ch == '{' || ch == '}' ||
ch == '[' || ch == ']' || ch == ':' || ch == ';' ||
ch == '<' || ch == '>' || ch == ',' || ch == '/' ||
ch == '?' || ch == '!' || ch == '.' || ch == '~')
return true;
return false;
}
// Simple case functions for ASCII supersets.
template <typename T>
constexpr T MakeUpperCase(T ch) noexcept {
if (ch < 'a' || ch > 'z')
return ch;
else
return ch - 'a' + 'A';
}
template <typename T>
constexpr T MakeLowerCase(T ch) noexcept {
if (ch < 'A' || ch > 'Z')
return ch;
else
return ch - 'A' + 'a';
}
int CompareCaseInsensitive(const char *a, const char *b) noexcept;
bool EqualCaseInsensitive(std::string_view a, std::string_view b) noexcept;
int CompareNCaseInsensitive(const char *a, const char *b, size_t len) noexcept;
}
#endif

View File

@ -0,0 +1,141 @@
// Scintilla source code edit control
/** @file DefaultLexer.cxx
** A lexer base class that provides reasonable default behaviour.
**/
// Copyright 2017 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstdlib>
#include <cassert>
#include <cstring>
#include <string>
#include <string_view>
#include "ILexer.h"
#include "Scintilla.h"
#include "SciLexer.h"
#include "PropSetSimple.h"
#include "WordList.h"
#include "LexAccessor.h"
#include "Accessor.h"
#include "LexerModule.h"
#include "DefaultLexer.h"
using namespace Lexilla;
static const char styleSubable[] = { 0 };
DefaultLexer::DefaultLexer(const char *languageName_, int language_,
const LexicalClass *lexClasses_, size_t nClasses_) :
languageName(languageName_),
language(language_),
lexClasses(lexClasses_),
nClasses(nClasses_) {
}
DefaultLexer::~DefaultLexer() = default;
void SCI_METHOD DefaultLexer::Release() {
delete this;
}
int SCI_METHOD DefaultLexer::Version() const {
return Scintilla::lvRelease5;
}
const char * SCI_METHOD DefaultLexer::PropertyNames() {
return "";
}
int SCI_METHOD DefaultLexer::PropertyType(const char *) {
return SC_TYPE_BOOLEAN;
}
const char * SCI_METHOD DefaultLexer::DescribeProperty(const char *) {
return "";
}
Sci_Position SCI_METHOD DefaultLexer::PropertySet(const char *, const char *) {
return -1;
}
const char * SCI_METHOD DefaultLexer::DescribeWordListSets() {
return "";
}
Sci_Position SCI_METHOD DefaultLexer::WordListSet(int, const char *) {
return -1;
}
void SCI_METHOD DefaultLexer::Fold(Sci_PositionU, Sci_Position, int, Scintilla::IDocument *) {
}
void * SCI_METHOD DefaultLexer::PrivateCall(int, void *) {
return nullptr;
}
int SCI_METHOD DefaultLexer::LineEndTypesSupported() {
return SC_LINE_END_TYPE_DEFAULT;
}
int SCI_METHOD DefaultLexer::AllocateSubStyles(int, int) {
return -1;
}
int SCI_METHOD DefaultLexer::SubStylesStart(int) {
return -1;
}
int SCI_METHOD DefaultLexer::SubStylesLength(int) {
return 0;
}
int SCI_METHOD DefaultLexer::StyleFromSubStyle(int subStyle) {
return subStyle;
}
int SCI_METHOD DefaultLexer::PrimaryStyleFromStyle(int style) {
return style;
}
void SCI_METHOD DefaultLexer::FreeSubStyles() {
}
void SCI_METHOD DefaultLexer::SetIdentifiers(int, const char *) {
}
int SCI_METHOD DefaultLexer::DistanceToSecondaryStyles() {
return 0;
}
const char * SCI_METHOD DefaultLexer::GetSubStyleBases() {
return styleSubable;
}
int SCI_METHOD DefaultLexer::NamedStyles() {
return static_cast<int>(nClasses);
}
const char * SCI_METHOD DefaultLexer::NameOfStyle(int style) {
return (style < NamedStyles()) ? lexClasses[style].name : "";
}
const char * SCI_METHOD DefaultLexer::TagsOfStyle(int style) {
return (style < NamedStyles()) ? lexClasses[style].tags : "";
}
const char * SCI_METHOD DefaultLexer::DescriptionOfStyle(int style) {
return (style < NamedStyles()) ? lexClasses[style].description : "";
}
// ILexer5 methods
const char * SCI_METHOD DefaultLexer::GetName() {
return languageName;
}
int SCI_METHOD DefaultLexer::GetIdentifier() {
return language;
}

View File

@ -0,0 +1,57 @@
// Scintilla source code edit control
/** @file DefaultLexer.h
** A lexer base class with default empty implementations of methods.
** For lexers that do not support all features so do not need real implementations.
** Does have real implementation for style metadata.
**/
// Copyright 2017 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef DEFAULTLEXER_H
#define DEFAULTLEXER_H
namespace Lexilla {
// A simple lexer with no state
class DefaultLexer : public Scintilla::ILexer5 {
const char *languageName;
int language;
const LexicalClass *lexClasses;
size_t nClasses;
public:
DefaultLexer(const char *languageName_, int language_,
const LexicalClass *lexClasses_ = nullptr, size_t nClasses_ = 0);
virtual ~DefaultLexer();
void SCI_METHOD Release() override;
int SCI_METHOD Version() const override;
const char * SCI_METHOD PropertyNames() override;
int SCI_METHOD PropertyType(const char *name) override;
const char * SCI_METHOD DescribeProperty(const char *name) override;
Sci_Position SCI_METHOD PropertySet(const char *key, const char *val) override;
const char * SCI_METHOD DescribeWordListSets() override;
Sci_Position SCI_METHOD WordListSet(int n, const char *wl) override;
void SCI_METHOD Lex(Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle, Scintilla::IDocument *pAccess) override = 0;
void SCI_METHOD Fold(Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle, Scintilla::IDocument *pAccess) override;
void * SCI_METHOD PrivateCall(int operation, void *pointer) override;
int SCI_METHOD LineEndTypesSupported() override;
int SCI_METHOD AllocateSubStyles(int styleBase, int numberStyles) override;
int SCI_METHOD SubStylesStart(int styleBase) override;
int SCI_METHOD SubStylesLength(int styleBase) override;
int SCI_METHOD StyleFromSubStyle(int subStyle) override;
int SCI_METHOD PrimaryStyleFromStyle(int style) override;
void SCI_METHOD FreeSubStyles() override;
void SCI_METHOD SetIdentifiers(int style, const char *identifiers) override;
int SCI_METHOD DistanceToSecondaryStyles() override;
const char * SCI_METHOD GetSubStyleBases() override;
int SCI_METHOD NamedStyles() override;
const char * SCI_METHOD NameOfStyle(int style) override;
const char * SCI_METHOD TagsOfStyle(int style) override;
const char * SCI_METHOD DescriptionOfStyle(int style) override;
// ILexer5 methods
const char * SCI_METHOD GetName() override;
int SCI_METHOD GetIdentifier() override;
};
}
#endif

View File

@ -0,0 +1,37 @@
// Scintilla source code edit control
/** @file InList.cxx
** Check if a string is in a list.
**/
// Copyright 2024 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cassert>
#include <string>
#include <string_view>
#include <initializer_list>
#include "InList.h"
#include "CharacterSet.h"
namespace Lexilla {
bool InList(std::string_view value, std::initializer_list<std::string_view> list) noexcept {
for (const std::string_view element : list) {
if (value == element) {
return true;
}
}
return false;
}
bool InListCaseInsensitive(std::string_view value, std::initializer_list<std::string_view> list) noexcept {
for (const std::string_view element : list) {
if (EqualCaseInsensitive(value, element)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,18 @@
// Scintilla source code edit control
/** @file InList.h
** Check if a string is in a list.
**/
// Copyright 2024 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef INLIST_H
#define INLIST_H
namespace Lexilla {
bool InList(std::string_view value, std::initializer_list<std::string_view> list) noexcept;
bool InListCaseInsensitive(std::string_view value, std::initializer_list<std::string_view> list) noexcept;
}
#endif

View File

@ -0,0 +1,73 @@
// Scintilla source code edit control
/** @file LexAccessor.cxx
** Interfaces between Scintilla and lexers.
**/
// Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cassert>
#include <cstring>
#include <string>
#include <algorithm>
#include "ILexer.h"
#include "LexAccessor.h"
#include "CharacterSet.h"
using namespace Lexilla;
namespace Lexilla {
bool LexAccessor::MatchIgnoreCase(Sci_Position pos, const char *s) {
assert(s);
for (; *s; s++, pos++) {
if (*s != MakeLowerCase(SafeGetCharAt(pos))) {
return false;
}
}
return true;
}
void LexAccessor::GetRange(Sci_PositionU startPos_, Sci_PositionU endPos_, char *s, Sci_PositionU len) {
assert(s);
assert(startPos_ <= endPos_ && len != 0);
endPos_ = std::min(endPos_, startPos_ + len - 1);
len = endPos_ - startPos_;
if (startPos_ >= static_cast<Sci_PositionU>(startPos) && endPos_ <= static_cast<Sci_PositionU>(endPos)) {
const char * const p = buf + (startPos_ - startPos);
memcpy(s, p, len);
} else {
pAccess->GetCharRange(s, startPos_, len);
}
s[len] = '\0';
}
void LexAccessor::GetRangeLowered(Sci_PositionU startPos_, Sci_PositionU endPos_, char *s, Sci_PositionU len) {
assert(s);
GetRange(startPos_, endPos_, s, len);
while (*s) {
if (*s >= 'A' && *s <= 'Z') {
*s += 'a' - 'A';
}
++s;
}
}
std::string LexAccessor::GetRange(Sci_PositionU startPos_, Sci_PositionU endPos_) {
assert(startPos_ < endPos_);
const Sci_PositionU len = endPos_ - startPos_;
std::string s(len, '\0');
GetRange(startPos_, endPos_, s.data(), len + 1);
return s;
}
std::string LexAccessor::GetRangeLowered(Sci_PositionU startPos_, Sci_PositionU endPos_) {
assert(startPos_ < endPos_);
const Sci_PositionU len = endPos_ - startPos_;
std::string s(len, '\0');
GetRangeLowered(startPos_, endPos_, s.data(), len + 1);
return s;
}
}

View File

@ -0,0 +1,227 @@
// Scintilla source code edit control
/** @file LexAccessor.h
** Interfaces between Scintilla and lexers.
**/
// Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef LEXACCESSOR_H
#define LEXACCESSOR_H
namespace Lexilla {
enum class EncodingType { eightBit, unicode, dbcs };
class LexAccessor {
private:
Scintilla::IDocument *pAccess;
enum {extremePosition=0x7FFFFFFF};
/** @a bufferSize is a trade off between time taken to copy the characters
* and retrieval overhead.
* @a slopSize positions the buffer before the desired position
* in case there is some backtracking. */
enum {bufferSize=4000, slopSize=bufferSize/8};
char buf[bufferSize+1];
Sci_Position startPos;
Sci_Position endPos;
int codePage;
enum EncodingType encodingType;
Sci_Position lenDoc;
char styleBuf[bufferSize];
Sci_Position validLen;
Sci_PositionU startSeg;
Sci_Position startPosStyling;
int documentVersion;
void Fill(Sci_Position position) {
startPos = position - slopSize;
if (startPos + bufferSize > lenDoc)
startPos = lenDoc - bufferSize;
if (startPos < 0)
startPos = 0;
endPos = startPos + bufferSize;
if (endPos > lenDoc)
endPos = lenDoc;
pAccess->GetCharRange(buf, startPos, endPos-startPos);
buf[endPos-startPos] = '\0';
}
public:
explicit LexAccessor(Scintilla::IDocument *pAccess_) :
pAccess(pAccess_), startPos(extremePosition), endPos(0),
codePage(pAccess->CodePage()),
encodingType(EncodingType::eightBit),
lenDoc(pAccess->Length()),
validLen(0),
startSeg(0), startPosStyling(0),
documentVersion(pAccess->Version()) {
// Prevent warnings by static analyzers about uninitialized buf and styleBuf.
buf[0] = 0;
styleBuf[0] = 0;
switch (codePage) {
case 65001:
encodingType = EncodingType::unicode;
break;
case 932:
case 936:
case 949:
case 950:
case 1361:
encodingType = EncodingType::dbcs;
break;
default:
break;
}
}
char operator[](Sci_Position position) {
if (position < startPos || position >= endPos) {
Fill(position);
}
return buf[position - startPos];
}
Scintilla::IDocument *MultiByteAccess() const noexcept {
return pAccess;
}
/** Safe version of operator[], returning a defined value for invalid position. */
char SafeGetCharAt(Sci_Position position, char chDefault=' ') {
if (position < startPos || position >= endPos) {
Fill(position);
if (position < startPos || position >= endPos) {
// Position is outside range of document
return chDefault;
}
}
return buf[position - startPos];
}
bool IsLeadByte(char ch) const {
const unsigned char uch = ch;
return
(uch >= 0x80) && // non-ASCII
(encodingType == EncodingType::dbcs) && // IsDBCSLeadByte only for DBCS
pAccess->IsDBCSLeadByte(ch);
}
EncodingType Encoding() const noexcept {
return encodingType;
}
bool Match(Sci_Position pos, const char *s) {
assert(s);
for (int i=0; *s; i++) {
if (*s != SafeGetCharAt(pos+i))
return false;
s++;
}
return true;
}
bool MatchIgnoreCase(Sci_Position pos, const char *s);
// Get first len - 1 characters in range [startPos_, endPos_).
void GetRange(Sci_PositionU startPos_, Sci_PositionU endPos_, char *s, Sci_PositionU len);
void GetRangeLowered(Sci_PositionU startPos_, Sci_PositionU endPos_, char *s, Sci_PositionU len);
// Get all characters in range [startPos_, endPos_).
std::string GetRange(Sci_PositionU startPos_, Sci_PositionU endPos_);
std::string GetRangeLowered(Sci_PositionU startPos_, Sci_PositionU endPos_);
char StyleAt(Sci_Position position) const {
return pAccess->StyleAt(position);
}
int StyleIndexAt(Sci_Position position) const {
const unsigned char style = pAccess->StyleAt(position);
return style;
}
// Return style value from buffer when in buffer, else retrieve from document.
// This is faster and can avoid calls to Flush() as that may be expensive.
int BufferStyleAt(Sci_Position position) const {
const Sci_Position index = position - startPosStyling;
if (index >= 0 && index < validLen) {
const unsigned char style = styleBuf[index];
return style;
}
const unsigned char style = pAccess->StyleAt(position);
return style;
}
Sci_Position GetLine(Sci_Position position) const {
return pAccess->LineFromPosition(position);
}
Sci_Position LineStart(Sci_Position line) const {
return pAccess->LineStart(line);
}
Sci_Position LineEnd(Sci_Position line) const {
return pAccess->LineEnd(line);
}
int LevelAt(Sci_Position line) const {
return pAccess->GetLevel(line);
}
Sci_Position Length() const noexcept {
return lenDoc;
}
void Flush() {
if (validLen > 0) {
pAccess->SetStyles(validLen, styleBuf);
startPosStyling += validLen;
validLen = 0;
}
}
int GetLineState(Sci_Position line) const {
return pAccess->GetLineState(line);
}
int SetLineState(Sci_Position line, int state) {
return pAccess->SetLineState(line, state);
}
// Style setting
void StartAt(Sci_PositionU start) {
pAccess->StartStyling(start);
startPosStyling = start;
}
Sci_PositionU GetStartSegment() const noexcept {
return startSeg;
}
void StartSegment(Sci_PositionU pos) noexcept {
startSeg = pos;
}
void ColourTo(Sci_PositionU pos, int chAttr) {
// Only perform styling if non empty range
if (pos != startSeg - 1) {
assert(pos >= startSeg);
if (pos < startSeg) {
return;
}
if (validLen + (pos - startSeg + 1) >= bufferSize)
Flush();
const unsigned char attr = chAttr & 0xffU;
if (validLen + (pos - startSeg + 1) >= bufferSize) {
// Too big for buffer so send directly
pAccess->SetStyleFor(pos - startSeg + 1, attr);
} else {
for (Sci_PositionU i = startSeg; i <= pos; i++) {
assert((startPosStyling + validLen) < Length());
styleBuf[validLen++] = attr;
}
}
}
startSeg = pos+1;
}
void SetLevel(Sci_Position line, int level) {
pAccess->SetLevel(line, level);
}
void IndicatorFill(Sci_Position start, Sci_Position end, int indicator, int value) {
pAccess->DecorationSetCurrentIndicator(indicator);
pAccess->DecorationFillRange(start, value, end - start);
}
void ChangeLexerState(Sci_Position start, Sci_Position end) {
pAccess->ChangeLexerState(start, end);
}
};
struct LexicalClass {
int value;
const char *name;
const char *tags;
const char *description;
};
}
#endif

View File

@ -0,0 +1,156 @@
// Scintilla source code edit control
/** @file LexerBase.cxx
** A simple lexer with no state.
**/
// Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstdlib>
#include <cassert>
#include <cstring>
#include <string>
#include <string_view>
#include "ILexer.h"
#include "Scintilla.h"
#include "SciLexer.h"
#include "PropSetSimple.h"
#include "WordList.h"
#include "LexAccessor.h"
#include "Accessor.h"
#include "LexerModule.h"
#include "LexerBase.h"
using namespace Lexilla;
static const char styleSubable[] = { 0 };
LexerBase::LexerBase(const LexicalClass *lexClasses_, size_t nClasses_) :
lexClasses(lexClasses_), nClasses(nClasses_) {
for (int wl = 0; wl < numWordLists; wl++)
keyWordLists[wl] = new WordList;
keyWordLists[numWordLists] = nullptr;
}
LexerBase::~LexerBase() {
for (int wl = 0; wl < numWordLists; wl++) {
delete keyWordLists[wl];
keyWordLists[wl] = nullptr;
}
keyWordLists[numWordLists] = nullptr;
}
void SCI_METHOD LexerBase::Release() {
delete this;
}
int SCI_METHOD LexerBase::Version() const {
return Scintilla::lvRelease5;
}
const char * SCI_METHOD LexerBase::PropertyNames() {
return "";
}
int SCI_METHOD LexerBase::PropertyType(const char *) {
return SC_TYPE_BOOLEAN;
}
const char * SCI_METHOD LexerBase::DescribeProperty(const char *) {
return "";
}
Sci_Position SCI_METHOD LexerBase::PropertySet(const char *key, const char *val) {
if (props.Set(key, val)) {
return 0;
} else {
return -1;
}
}
const char *SCI_METHOD LexerBase::PropertyGet(const char *key) {
return props.Get(key);
}
const char * SCI_METHOD LexerBase::DescribeWordListSets() {
return "";
}
Sci_Position SCI_METHOD LexerBase::WordListSet(int n, const char *wl) {
if (n < numWordLists) {
if (keyWordLists[n]->Set(wl)) {
return 0;
}
}
return -1;
}
void * SCI_METHOD LexerBase::PrivateCall(int, void *) {
return nullptr;
}
int SCI_METHOD LexerBase::LineEndTypesSupported() {
return SC_LINE_END_TYPE_DEFAULT;
}
int SCI_METHOD LexerBase::AllocateSubStyles(int, int) {
return -1;
}
int SCI_METHOD LexerBase::SubStylesStart(int) {
return -1;
}
int SCI_METHOD LexerBase::SubStylesLength(int) {
return 0;
}
int SCI_METHOD LexerBase::StyleFromSubStyle(int subStyle) {
return subStyle;
}
int SCI_METHOD LexerBase::PrimaryStyleFromStyle(int style) {
return style;
}
void SCI_METHOD LexerBase::FreeSubStyles() {
}
void SCI_METHOD LexerBase::SetIdentifiers(int, const char *) {
}
int SCI_METHOD LexerBase::DistanceToSecondaryStyles() {
return 0;
}
const char * SCI_METHOD LexerBase::GetSubStyleBases() {
return styleSubable;
}
int SCI_METHOD LexerBase::NamedStyles() {
return static_cast<int>(nClasses);
}
const char * SCI_METHOD LexerBase::NameOfStyle(int style) {
return (style < NamedStyles()) ? lexClasses[style].name : "";
}
const char * SCI_METHOD LexerBase::TagsOfStyle(int style) {
return (style < NamedStyles()) ? lexClasses[style].tags : "";
}
const char * SCI_METHOD LexerBase::DescriptionOfStyle(int style) {
return (style < NamedStyles()) ? lexClasses[style].description : "";
}
// ILexer5 methods
const char *SCI_METHOD LexerBase::GetName() {
return "";
}
int SCI_METHOD LexerBase::GetIdentifier() {
return SCLEX_AUTOMATIC;
}

View File

@ -0,0 +1,57 @@
// Scintilla source code edit control
/** @file LexerBase.h
** A simple lexer with no state.
**/
// Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef LEXERBASE_H
#define LEXERBASE_H
namespace Lexilla {
// A simple lexer with no state
class LexerBase : public Scintilla::ILexer5 {
protected:
const LexicalClass *lexClasses;
size_t nClasses;
PropSetSimple props;
enum {numWordLists=KEYWORDSET_MAX+1};
WordList *keyWordLists[numWordLists+1];
public:
LexerBase(const LexicalClass *lexClasses_=nullptr, size_t nClasses_=0);
virtual ~LexerBase();
void SCI_METHOD Release() override;
int SCI_METHOD Version() const override;
const char * SCI_METHOD PropertyNames() override;
int SCI_METHOD PropertyType(const char *name) override;
const char * SCI_METHOD DescribeProperty(const char *name) override;
Sci_Position SCI_METHOD PropertySet(const char *key, const char *val) override;
const char * SCI_METHOD DescribeWordListSets() override;
Sci_Position SCI_METHOD WordListSet(int n, const char *wl) override;
void SCI_METHOD Lex(Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle, Scintilla::IDocument *pAccess) override = 0;
void SCI_METHOD Fold(Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle, Scintilla::IDocument *pAccess) override = 0;
void * SCI_METHOD PrivateCall(int operation, void *pointer) override;
int SCI_METHOD LineEndTypesSupported() override;
int SCI_METHOD AllocateSubStyles(int styleBase, int numberStyles) override;
int SCI_METHOD SubStylesStart(int styleBase) override;
int SCI_METHOD SubStylesLength(int styleBase) override;
int SCI_METHOD StyleFromSubStyle(int subStyle) override;
int SCI_METHOD PrimaryStyleFromStyle(int style) override;
void SCI_METHOD FreeSubStyles() override;
void SCI_METHOD SetIdentifiers(int style, const char *identifiers) override;
int SCI_METHOD DistanceToSecondaryStyles() override;
const char * SCI_METHOD GetSubStyleBases() override;
int SCI_METHOD NamedStyles() override;
const char * SCI_METHOD NameOfStyle(int style) override;
const char * SCI_METHOD TagsOfStyle(int style) override;
const char * SCI_METHOD DescriptionOfStyle(int style) override;
// ILexer5 methods
const char * SCI_METHOD GetName() override;
int SCI_METHOD GetIdentifier() override;
const char *SCI_METHOD PropertyGet(const char *key) override;
};
}
#endif

View File

@ -0,0 +1,124 @@
// Scintilla source code edit control
/** @file LexerModule.cxx
** Colourise for particular languages.
**/
// Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstdlib>
#include <cassert>
#include <string>
#include <string_view>
#include "ILexer.h"
#include "Scintilla.h"
#include "SciLexer.h"
#include "PropSetSimple.h"
#include "WordList.h"
#include "LexAccessor.h"
#include "Accessor.h"
#include "LexerModule.h"
#include "LexerBase.h"
#include "LexerSimple.h"
using namespace Lexilla;
LexerModule::LexerModule(int language_,
LexerFunction fnLexer_,
const char *languageName_,
LexerFunction fnFolder_,
const char *const wordListDescriptions_[],
const LexicalClass *lexClasses_,
size_t nClasses_) noexcept :
language(language_),
fnLexer(fnLexer_),
fnFolder(fnFolder_),
fnFactory(nullptr),
wordListDescriptions(wordListDescriptions_),
lexClasses(lexClasses_),
nClasses(nClasses_),
languageName(languageName_) {
}
LexerModule::LexerModule(int language_,
LexerFactoryFunction fnFactory_,
const char *languageName_,
const char * const wordListDescriptions_[]) noexcept :
language(language_),
fnLexer(nullptr),
fnFolder(nullptr),
fnFactory(fnFactory_),
wordListDescriptions(wordListDescriptions_),
lexClasses(nullptr),
nClasses(0),
languageName(languageName_) {
}
int LexerModule::GetLanguage() const noexcept {
return language;
}
int LexerModule::GetNumWordLists() const noexcept {
if (!wordListDescriptions) {
return -1;
} else {
int numWordLists = 0;
while (wordListDescriptions[numWordLists]) {
++numWordLists;
}
return numWordLists;
}
}
const char *LexerModule::GetWordListDescription(int index) const noexcept {
assert(index < GetNumWordLists());
if (!wordListDescriptions || (index >= GetNumWordLists())) {
return "";
} else {
return wordListDescriptions[index];
}
}
const LexicalClass *LexerModule::LexClasses() const noexcept {
return lexClasses;
}
size_t LexerModule::NamedStyles() const noexcept {
return nClasses;
}
Scintilla::ILexer5 *LexerModule::Create() const {
if (fnFactory)
return fnFactory();
else
return new LexerSimple(this);
}
void LexerModule::Lex(Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle,
WordList *keywordlists[], Accessor &styler) const {
if (fnLexer)
fnLexer(startPos, lengthDoc, initStyle, keywordlists, styler);
}
void LexerModule::Fold(Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle,
WordList *keywordlists[], Accessor &styler) const {
if (fnFolder) {
Sci_Position lineCurrent = styler.GetLine(startPos);
// Move back one line in case deletion wrecked current line fold state
if (lineCurrent > 0) {
lineCurrent--;
const Sci_Position newStartPos = styler.LineStart(lineCurrent);
lengthDoc += startPos - newStartPos;
startPos = newStartPos;
initStyle = 0;
if (startPos > 0) {
initStyle = styler.StyleIndexAt(startPos - 1);
}
}
fnFolder(startPos, lengthDoc, initStyle, keywordlists, styler);
}
}

View File

@ -0,0 +1,92 @@
// Scintilla source code edit control
/** @file LexerModule.h
** Colourise for particular languages.
**/
// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef LEXERMODULE_H
#define LEXERMODULE_H
namespace Lexilla {
class Accessor;
class WordList;
struct LexicalClass;
typedef void (*LexerFunction)(Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle,
WordList *keywordlists[], Accessor &styler);
typedef Scintilla::ILexer5 *(*LexerFactoryFunction)();
/**
* A LexerModule is responsible for lexing and folding a particular language.
* The Catalogue class maintains a list of LexerModules which can be searched to find a
* module appropriate to a particular language.
* The ExternalLexerModule subclass holds lexers loaded from DLLs or shared libraries.
*/
class LexerModule {
protected:
int language;
LexerFunction fnLexer;
LexerFunction fnFolder;
LexerFactoryFunction fnFactory;
const char * const * wordListDescriptions;
const LexicalClass *lexClasses;
size_t nClasses;
public:
const char *languageName;
LexerModule(
int language_,
LexerFunction fnLexer_,
const char *languageName_=nullptr,
LexerFunction fnFolder_= nullptr,
const char * const wordListDescriptions_[]=nullptr,
const LexicalClass *lexClasses_=nullptr,
size_t nClasses_=0) noexcept;
LexerModule(
int language_,
LexerFactoryFunction fnFactory_,
const char *languageName_,
const char * const wordListDescriptions_[]=nullptr) noexcept;
int GetLanguage() const noexcept;
// -1 is returned if no WordList information is available
int GetNumWordLists() const noexcept;
const char *GetWordListDescription(int index) const noexcept;
const LexicalClass *LexClasses() const noexcept;
size_t NamedStyles() const noexcept;
Scintilla::ILexer5 *Create() const;
void Lex(Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle,
WordList *keywordlists[], Accessor &styler) const;
void Fold(Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle,
WordList *keywordlists[], Accessor &styler) const;
friend class CatalogueModules;
};
constexpr int Maximum(int a, int b) noexcept {
return (a > b) ? a : b;
}
// Shut up annoying Visual C++ warnings:
#if defined(_MSC_VER)
#pragma warning(disable: 4244 4456 4457)
#endif
// Turn off shadow warnings for lexers as may be maintained by others
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wshadow"
#endif
// Clang doesn't like omitting braces in array initialization but they just add
// noise to LexicalClass arrays in lexers
#if defined(__clang__)
#pragma clang diagnostic ignored "-Wmissing-braces"
#endif
}
#endif

View File

@ -0,0 +1,62 @@
// Scintilla source code edit control
/** @file LexerSimple.cxx
** A simple lexer with no state.
**/
// Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstdlib>
#include <cassert>
#include <string>
#include <string_view>
#include "ILexer.h"
#include "Scintilla.h"
#include "SciLexer.h"
#include "PropSetSimple.h"
#include "WordList.h"
#include "LexAccessor.h"
#include "Accessor.h"
#include "LexerModule.h"
#include "LexerBase.h"
#include "LexerSimple.h"
using namespace Lexilla;
LexerSimple::LexerSimple(const LexerModule *module_) :
LexerBase(module_->LexClasses(), module_->NamedStyles()),
lexerModule(module_) {
for (int wl = 0; wl < lexerModule->GetNumWordLists(); wl++) {
if (!wordLists.empty())
wordLists += "\n";
wordLists += lexerModule->GetWordListDescription(wl);
}
}
const char * SCI_METHOD LexerSimple::DescribeWordListSets() {
return wordLists.c_str();
}
void SCI_METHOD LexerSimple::Lex(Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle, Scintilla::IDocument *pAccess) {
Accessor astyler(pAccess, &props);
lexerModule->Lex(startPos, lengthDoc, initStyle, keyWordLists, astyler);
astyler.Flush();
}
void SCI_METHOD LexerSimple::Fold(Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle, Scintilla::IDocument *pAccess) {
if (props.GetInt("fold")) {
Accessor astyler(pAccess, &props);
lexerModule->Fold(startPos, lengthDoc, initStyle, keyWordLists, astyler);
astyler.Flush();
}
}
const char * SCI_METHOD LexerSimple::GetName() {
return lexerModule->languageName;
}
int SCI_METHOD LexerSimple::GetIdentifier() {
return lexerModule->GetLanguage();
}

View File

@ -0,0 +1,29 @@
// Scintilla source code edit control
/** @file LexerSimple.h
** A simple lexer with no state.
**/
// Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef LEXERSIMPLE_H
#define LEXERSIMPLE_H
namespace Lexilla {
// A simple lexer with no state
class LexerSimple : public LexerBase {
const LexerModule *lexerModule;
std::string wordLists;
public:
explicit LexerSimple(const LexerModule *lexerModule_);
const char * SCI_METHOD DescribeWordListSets() override;
void SCI_METHOD Lex(Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle, Scintilla::IDocument *pAccess) override;
void SCI_METHOD Fold(Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle, Scintilla::IDocument *pAccess) override;
// ILexer5 methods
const char * SCI_METHOD GetName() override;
int SCI_METHOD GetIdentifier() override;
};
}
#endif

View File

@ -0,0 +1,160 @@
// Scintilla source code edit control
/** @file OptionSet.h
** Manage descriptive information about an options struct for a lexer.
** Hold the names, positions, and descriptions of boolean, integer and string options and
** allow setting options and retrieving metadata about the options.
**/
// Copyright 2010 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef OPTIONSET_H
#define OPTIONSET_H
namespace Lexilla {
template <typename T>
class OptionSet {
typedef T Target;
typedef bool T::*plcob;
typedef int T::*plcoi;
typedef std::string T::*plcos;
struct Option {
int opType;
union {
plcob pb;
plcoi pi;
plcos ps;
};
std::string value;
std::string description;
Option() :
opType(SC_TYPE_BOOLEAN), pb(nullptr) {
}
Option(plcob pb_, std::string_view description_="") :
opType(SC_TYPE_BOOLEAN), pb(pb_), description(description_) {
}
Option(plcoi pi_, std::string_view description_) :
opType(SC_TYPE_INTEGER), pi(pi_), description(description_) {
}
Option(plcos ps_, std::string_view description_) :
opType(SC_TYPE_STRING), ps(ps_), description(description_) {
}
bool Set(T *base, const char *val) {
value = val;
switch (opType) {
case SC_TYPE_BOOLEAN: {
const bool option = atoi(val) != 0;
if ((*base).*pb != option) {
(*base).*pb = option;
return true;
}
break;
}
case SC_TYPE_INTEGER: {
const int option = atoi(val);
if ((*base).*pi != option) {
(*base).*pi = option;
return true;
}
break;
}
case SC_TYPE_STRING: {
if ((*base).*ps != val) {
(*base).*ps = val;
return true;
}
break;
}
default:
break;
}
return false;
}
const char *Get() const noexcept {
return value.c_str();
}
};
typedef std::map<std::string, Option, std::less<>> OptionMap;
OptionMap nameToDef;
std::string names;
std::string wordLists;
void AppendName(const char *name) {
if (!names.empty())
names += "\n";
names += name;
}
public:
void DefineProperty(const char *name, plcob pb, std::string_view description="") {
nameToDef[name] = Option(pb, description);
AppendName(name);
}
void DefineProperty(const char *name, plcoi pi, std::string_view description="") {
nameToDef[name] = Option(pi, description);
AppendName(name);
}
void DefineProperty(const char *name, plcos ps, std::string_view description="") {
nameToDef[name] = Option(ps, description);
AppendName(name);
}
template <typename E>
void DefineProperty(const char *name, E T::*pe, std::string_view description="") {
static_assert(std::is_enum<E>::value);
plcoi pi {};
static_assert(sizeof(pe) == sizeof(pi));
memcpy(&pi, &pe, sizeof(pe));
nameToDef[name] = Option(pi, description);
AppendName(name);
}
const char *PropertyNames() const noexcept {
return names.c_str();
}
int PropertyType(const char *name) const {
typename OptionMap::const_iterator const it = nameToDef.find(name);
if (it != nameToDef.end()) {
return it->second.opType;
}
return SC_TYPE_BOOLEAN;
}
const char *DescribeProperty(const char *name) const {
typename OptionMap::const_iterator const it = nameToDef.find(name);
if (it != nameToDef.end()) {
return it->second.description.c_str();
}
return "";
}
bool PropertySet(T *base, const char *name, const char *val) {
typename OptionMap::iterator const it = nameToDef.find(name);
if (it != nameToDef.end()) {
return it->second.Set(base, val);
}
return false;
}
const char *PropertyGet(const char *name) const {
typename OptionMap::const_iterator const it = nameToDef.find(name);
if (it != nameToDef.end()) {
return it->second.Get();
}
return nullptr;
}
void DefineWordListSets(const char * const wordListDescriptions[]) {
if (wordListDescriptions) {
for (size_t wl = 0; wordListDescriptions[wl]; wl++) {
if (wl > 0)
wordLists += "\n";
wordLists += wordListDescriptions[wl];
}
}
}
const char *DescribeWordListSets() const noexcept {
return wordLists.c_str();
}
};
}
#endif

View File

@ -0,0 +1,77 @@
// Scintilla source code edit control
/** @file PropSetSimple.cxx
** A basic string to string map.
**/
// Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
// Maintain a dictionary of properties
#include <cstdlib>
#include <cassert>
#include <cstring>
#include <string>
#include <string_view>
#include <map>
#include <functional>
#include "PropSetSimple.h"
using namespace Lexilla;
namespace {
using mapss = std::map<std::string, std::string, std::less<>>;
mapss *PropsFromPointer(void *impl) noexcept {
return static_cast<mapss *>(impl);
}
}
PropSetSimple::PropSetSimple() {
mapss *props = new mapss;
impl = static_cast<void *>(props);
}
PropSetSimple::~PropSetSimple() {
mapss *props = PropsFromPointer(impl);
delete props;
impl = nullptr;
}
bool PropSetSimple::Set(std::string_view key, std::string_view val) {
mapss *props = PropsFromPointer(impl);
if (!props)
return false;
mapss::iterator const it = props->find(key);
if (it != props->end()) {
if (val == it->second)
return false;
it->second = val;
} else {
props->emplace(key, val);
}
return true;
}
const char *PropSetSimple::Get(std::string_view key) const {
mapss *props = PropsFromPointer(impl);
if (props) {
mapss::const_iterator const keyPos = props->find(key);
if (keyPos != props->end()) {
return keyPos->second.c_str();
}
}
return "";
}
int PropSetSimple::GetInt(std::string_view key, int defaultValue) const {
const char *val = Get(key);
assert(val);
if (*val) {
return atoi(val);
}
return defaultValue;
}

View File

@ -0,0 +1,31 @@
// Scintilla source code edit control
/** @file PropSetSimple.h
** A basic string to string map.
**/
// Copyright 1998-2009 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef PROPSETSIMPLE_H
#define PROPSETSIMPLE_H
namespace Lexilla {
class PropSetSimple {
void *impl;
public:
PropSetSimple();
// Deleted so PropSetSimple objects can not be copied.
PropSetSimple(const PropSetSimple&) = delete;
PropSetSimple(PropSetSimple&&) = delete;
PropSetSimple &operator=(const PropSetSimple&) = delete;
PropSetSimple &operator=(PropSetSimple&&) = delete;
virtual ~PropSetSimple();
bool Set(std::string_view key, std::string_view val);
const char *Get(std::string_view key) const;
int GetInt(std::string_view key, int defaultValue=0) const;
};
}
#endif

View File

@ -0,0 +1,107 @@
// Scintilla source code edit control
/** @file SparseState.h
** Hold lexer state that may change rarely.
** This is often per-line state such as whether a particular type of section has been entered.
** A state continues until it is changed.
**/
// Copyright 2011 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef SPARSESTATE_H
#define SPARSESTATE_H
namespace Lexilla {
template <typename T>
class SparseState {
struct State {
Sci_Position position;
T value;
constexpr State(Sci_Position position_, T value_) noexcept :
position(position_), value(std::move(value_)) {
}
inline bool operator<(const State &other) const noexcept {
return position < other.position;
}
inline bool operator==(const State &other) const noexcept {
return (position == other.position) && (value == other.value);
}
};
Sci_Position positionFirst;
typedef std::vector<State> stateVector;
stateVector states;
typename stateVector::iterator Find(Sci_Position position) {
const State searchValue(position, T());
return std::lower_bound(states.begin(), states.end(), searchValue);
}
public:
explicit SparseState(Sci_Position positionFirst_=-1) {
positionFirst = positionFirst_;
}
void Set(Sci_Position position, T value) {
Delete(position);
if (states.empty() || (value != states[states.size()-1].value)) {
states.push_back(State(position, value));
}
}
T ValueAt(Sci_Position position) {
if (states.empty())
return T();
if (position < states[0].position)
return T();
typename stateVector::iterator low = Find(position);
if (low == states.end()) {
return states[states.size()-1].value;
} else {
if (low->position > position) {
--low;
}
return low->value;
}
}
bool Delete(Sci_Position position) {
typename stateVector::iterator low = Find(position);
if (low != states.end()) {
states.erase(low, states.end());
return true;
}
return false;
}
size_t size() const {
return states.size();
}
// Returns true if Merge caused a significant change
bool Merge(const SparseState<T> &other, Sci_Position ignoreAfter) {
// Changes caused beyond ignoreAfter are not significant
Delete(ignoreAfter+1);
bool different = true;
bool changed = false;
typename stateVector::iterator low = Find(other.positionFirst);
if (static_cast<size_t>(states.end() - low) == other.states.size()) {
// Same number in other as after positionFirst in this
different = !std::equal(low, states.end(), other.states.begin());
}
if (different) {
if (low != states.end()) {
states.erase(low, states.end());
changed = true;
}
typename stateVector::const_iterator startOther = other.states.begin();
if (!states.empty() && !other.states.empty() && states.back().value == startOther->value)
++startOther;
if (startOther != other.states.end()) {
states.insert(states.end(), startOther, other.states.end());
changed = true;
}
}
return changed;
}
};
}
#endif

View File

@ -0,0 +1,32 @@
// Scintilla source code edit control
/** @file StringCopy.h
** Safe string copy function which always NUL terminates.
** ELEMENTS macro for determining array sizes.
**/
// Copyright 2013 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef STRINGCOPY_H
#define STRINGCOPY_H
namespace Lexilla {
// Safer version of string copy functions like strcpy, wcsncpy, etc.
// Instantiate over fixed length strings of both char and wchar_t.
// May truncate if source doesn't fit into dest with room for NUL.
template <typename T, size_t count>
void StringCopy(T (&dest)[count], const T* source) {
for (size_t i=0; i<count; i++) {
dest[i] = source[i];
if (!source[i])
break;
}
dest[count-1] = 0;
}
#define ELEMENTS(a) (sizeof(a) / sizeof(a[0]))
}
#endif

View File

@ -0,0 +1,86 @@
// Scintilla source code edit control
/** @file StyleContext.cxx
** Lexer infrastructure.
**/
// Copyright 1998-2004 by Neil Hodgson <neilh@scintilla.org>
// This file is in the public domain.
#include <cstdlib>
#include <cstdint>
#include <cassert>
#include <string>
#include <string_view>
#include "ILexer.h"
#include "LexAccessor.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "CharacterSet.h"
using namespace Lexilla;
StyleContext::StyleContext(Sci_PositionU startPos, Sci_PositionU length,
int initStyle, LexAccessor &styler_, char chMask) :
styler(styler_),
multiByteAccess((styler.Encoding() == EncodingType::eightBit) ? nullptr : styler.MultiByteAccess()),
lengthDocument(static_cast<Sci_PositionU>(styler.Length())),
endPos(((startPos + length) < lengthDocument) ? (startPos + length) : (lengthDocument+1)),
lineDocEnd(styler.GetLine(lengthDocument)),
currentPosLastRelative(SIZE_MAX),
currentPos(startPos),
currentLine(styler.GetLine(startPos)),
lineEnd(styler.LineEnd(currentLine)),
lineStartNext(styler.LineStart(currentLine + 1)),
atLineStart(static_cast<Sci_PositionU>(styler.LineStart(currentLine)) == startPos),
// Mask off all bits which aren't in the chMask.
state(initStyle &chMask) {
styler.StartAt(startPos /*, chMask*/);
styler.StartSegment(startPos);
chPrev = GetRelativeCharacter(-1);
// Variable width is now 0 so GetNextChar gets the char at currentPos into chNext/widthNext
GetNextChar();
ch = chNext;
width = widthNext;
GetNextChar();
}
bool StyleContext::MatchIgnoreCase(const char *s) {
if (MakeLowerCase(ch) != static_cast<unsigned char>(*s))
return false;
s++;
if (MakeLowerCase(chNext) != static_cast<unsigned char>(*s))
return false;
s++;
for (int n = 2; *s; n++) {
if (*s !=
MakeLowerCase(styler.SafeGetCharAt(currentPos + n, 0)))
return false;
s++;
}
return true;
}
void StyleContext::GetCurrent(char *s, Sci_PositionU len) {
styler.GetRange(styler.GetStartSegment(), currentPos, s, len);
}
void StyleContext::GetCurrentLowered(char *s, Sci_PositionU len) {
styler.GetRangeLowered(styler.GetStartSegment(), currentPos, s, len);
}
void StyleContext::GetCurrentString(std::string &string, Transform transform) {
const Sci_PositionU startPos = styler.GetStartSegment();
const Sci_PositionU len = currentPos - styler.GetStartSegment();
string.resize(len);
if (transform == Transform::lower) {
styler.GetRangeLowered(startPos, currentPos, string.data(), len + 1);
} else {
styler.GetRange(startPos, currentPos, string.data(), len + 1);
}
}

View File

@ -0,0 +1,194 @@
// Scintilla source code edit control
/** @file StyleContext.h
** Lexer infrastructure.
**/
// Copyright 1998-2004 by Neil Hodgson <neilh@scintilla.org>
// This file is in the public domain.
#ifndef STYLECONTEXT_H
#define STYLECONTEXT_H
namespace Lexilla {
// All languages handled so far can treat all characters >= 0x80 as one class
// which just continues the current token or starts an identifier if in default.
// DBCS treated specially as the second character can be < 0x80 and hence
// syntactically significant. UTF-8 avoids this as all trail bytes are >= 0x80
class StyleContext {
LexAccessor &styler;
Scintilla::IDocument * const multiByteAccess;
const Sci_PositionU lengthDocument;
const Sci_PositionU endPos;
const Sci_Position lineDocEnd;
// Used for optimizing GetRelativeCharacter
Sci_PositionU posRelative = 0;
Sci_PositionU currentPosLastRelative;
Sci_Position offsetRelative = 0;
void GetNextChar() {
if (multiByteAccess) {
chNext = multiByteAccess->GetCharacterAndWidth(currentPos+width, &widthNext);
} else {
const unsigned char charNext = styler.SafeGetCharAt(currentPos + width, 0);
chNext = charNext;
}
// End of line determined from line end position, allowing CR, LF,
// CRLF and Unicode line ends as set by document.
const Sci_Position currentPosSigned = currentPos;
if (currentLine < lineDocEnd)
atLineEnd = currentPosSigned >= (lineStartNext-1);
else // Last line
atLineEnd = currentPosSigned >= lineStartNext;
}
public:
Sci_PositionU currentPos;
Sci_Position currentLine;
Sci_Position lineEnd;
Sci_Position lineStartNext;
bool atLineStart;
bool atLineEnd = false;
int state;
int chPrev = 0;
int ch = 0;
Sci_Position width = 0;
int chNext = 0;
Sci_Position widthNext = 1;
StyleContext(Sci_PositionU startPos, Sci_PositionU length,
int initStyle, LexAccessor &styler_, char chMask = '\377');
// Deleted so StyleContext objects can not be copied.
StyleContext(const StyleContext &) = delete;
StyleContext &operator=(const StyleContext &) = delete;
void Complete() {
styler.ColourTo(currentPos - ((currentPos > lengthDocument) ? 2 : 1), state);
styler.Flush();
}
bool More() const noexcept {
return currentPos < endPos;
}
void Forward() {
if (currentPos < endPos) {
atLineStart = atLineEnd;
if (atLineStart) {
currentLine++;
lineEnd = styler.LineEnd(currentLine);
lineStartNext = styler.LineStart(currentLine+1);
}
chPrev = ch;
currentPos += width;
ch = chNext;
width = widthNext;
GetNextChar();
} else {
atLineStart = false;
chPrev = ' ';
ch = ' ';
chNext = ' ';
atLineEnd = true;
}
}
void Forward(Sci_Position nb) {
for (Sci_Position i = 0; i < nb; i++) {
Forward();
}
}
void ForwardBytes(Sci_Position nb) {
const Sci_PositionU forwardPos = currentPos + nb;
while (forwardPos > currentPos) {
const Sci_PositionU currentPosStart = currentPos;
Forward();
if (currentPos == currentPosStart) {
// Reached end
return;
}
}
}
void ChangeState(int state_) noexcept {
state = state_;
}
void SetState(int state_) {
styler.ColourTo(currentPos - ((currentPos > lengthDocument) ? 2 : 1), state);
state = state_;
}
void ForwardSetState(int state_) {
Forward();
styler.ColourTo(currentPos - ((currentPos > lengthDocument) ? 2 : 1), state);
state = state_;
}
Sci_Position LengthCurrent() const noexcept {
return currentPos - styler.GetStartSegment();
}
char GetRelativeChar(Sci_Position n, char chDefault='\0') {
return styler.SafeGetCharAt(currentPos + n, chDefault);
}
int GetRelative(Sci_Position n, char chDefault='\0') {
const unsigned char chRelative = styler.SafeGetCharAt(currentPos + n, chDefault);
return chRelative;
}
int GetRelativeCharacter(Sci_Position n) {
if (n == 0)
return ch;
if (multiByteAccess) {
if ((currentPosLastRelative != currentPos) ||
((n > 0) && ((offsetRelative < 0) || (n < offsetRelative))) ||
((n < 0) && ((offsetRelative > 0) || (n > offsetRelative)))) {
posRelative = currentPos;
offsetRelative = 0;
}
const Sci_Position diffRelative = n - offsetRelative;
const Sci_Position posNew = multiByteAccess->GetRelativePosition(posRelative, diffRelative);
const int chReturn = multiByteAccess->GetCharacterAndWidth(posNew, nullptr);
posRelative = posNew;
currentPosLastRelative = currentPos;
offsetRelative = n;
return chReturn;
} else {
// fast version for single byte encodings
const unsigned char chRelative = styler.SafeGetCharAt(currentPos + n, 0);
return chRelative;
}
}
bool MatchLineEnd() const noexcept {
const Sci_Position currentPosSigned = currentPos;
return currentPosSigned == lineEnd;
}
bool Match(char ch0) const noexcept {
const unsigned char uch0 = ch0;
return ch == uch0;
}
bool Match(char ch0, char ch1) const noexcept {
const unsigned char uch0 = ch0;
const unsigned char uch1 = ch1;
return (ch == uch0) && (chNext == uch1);
}
bool Match(const char *s) {
const unsigned char su = *s;
if (ch != su)
return false;
s++;
if (!*s)
return true;
const unsigned char sNext = *s;
if (chNext != sNext)
return false;
s++;
for (int n=2; *s; n++) {
if (*s != styler.SafeGetCharAt(currentPos+n, 0))
return false;
s++;
}
return true;
}
// Non-inline
bool MatchIgnoreCase(const char *s);
void GetCurrent(char *s, Sci_PositionU len);
void GetCurrentLowered(char *s, Sci_PositionU len);
enum class Transform { none, lower };
void GetCurrentString(std::string &string, Transform transform);
};
}
#endif

View File

@ -0,0 +1,221 @@
// Scintilla source code edit control
/** @file SubStyles.h
** Manage substyles for a lexer.
**/
// Copyright 2012 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef SUBSTYLES_H
#define SUBSTYLES_H
namespace Lexilla {
class WordClassifier {
int baseStyle;
int firstStyle;
int lenStyles;
using WordStyleMap = std::map<std::string, int, std::less<>>;
WordStyleMap wordToStyle;
public:
explicit WordClassifier(int baseStyle_) : baseStyle(baseStyle_), firstStyle(0), lenStyles(0) {
}
void Allocate(int firstStyle_, int lenStyles_) noexcept {
firstStyle = firstStyle_;
lenStyles = lenStyles_;
wordToStyle.clear();
}
int Base() const noexcept {
return baseStyle;
}
int Start() const noexcept {
return firstStyle;
}
int Last() const noexcept {
return firstStyle + lenStyles - 1;
}
int Length() const noexcept {
return lenStyles;
}
void Clear() noexcept {
firstStyle = 0;
lenStyles = 0;
wordToStyle.clear();
}
int ValueFor(std::string_view s) const {
WordStyleMap::const_iterator const it = wordToStyle.find(s);
if (it != wordToStyle.end())
return it->second;
else
return -1;
}
bool IncludesStyle(int style) const noexcept {
return (style >= firstStyle) && (style < (firstStyle + lenStyles));
}
void RemoveStyle(int style) noexcept {
WordStyleMap::iterator it = wordToStyle.begin();
while (it != wordToStyle.end()) {
if (it->second == style) {
it = wordToStyle.erase(it);
} else {
++it;
}
}
}
void SetIdentifiers(int style, const char *identifiers, bool lowerCase) {
RemoveStyle(style);
if (!identifiers)
return;
while (*identifiers) {
const char *cpSpace = identifiers;
while (*cpSpace && !(*cpSpace == ' ' || *cpSpace == '\t' || *cpSpace == '\r' || *cpSpace == '\n'))
cpSpace++;
if (cpSpace > identifiers) {
std::string word(identifiers, cpSpace - identifiers);
if (lowerCase) {
for (char &ch : word) {
ch = MakeLowerCase(ch);
}
}
wordToStyle[word] = style;
}
identifiers = cpSpace;
if (*identifiers)
identifiers++;
}
}
};
// This is the common configuration: 64 sub-styles allocated from 128 to 191
constexpr int SubStylesFirst = 0x80;
constexpr int SubStylesAvailable = 0x40;
class SubStyles {
int classifications;
const char *baseStyles;
int styleFirst;
int stylesAvailable;
int secondaryDistance;
int allocated;
std::vector<WordClassifier> classifiers;
int BlockFromBaseStyle(int baseStyle) const noexcept {
for (int b=0; b < classifications; b++) {
if (baseStyle == baseStyles[b])
return b;
}
return -1;
}
int BlockFromStyle(int style) const noexcept {
int b = 0;
for (const WordClassifier &wc : classifiers) {
if (wc.IncludesStyle(style))
return b;
b++;
}
return -1;
}
public:
SubStyles(const char *baseStyles_, int styleFirst_=SubStylesFirst, int stylesAvailable_=SubStylesAvailable, int secondaryDistance_=0) :
classifications(0),
baseStyles(baseStyles_),
styleFirst(styleFirst_),
stylesAvailable(stylesAvailable_),
secondaryDistance(secondaryDistance_),
allocated(0) {
while (baseStyles[classifications]) {
classifiers.push_back(WordClassifier(baseStyles[classifications]));
classifications++;
}
}
int Allocate(int styleBase, int numberStyles) noexcept {
const int block = BlockFromBaseStyle(styleBase);
if (block >= 0) {
if ((allocated + numberStyles) > stylesAvailable)
return -1;
const int startBlock = styleFirst + allocated;
allocated += numberStyles;
classifiers[block].Allocate(startBlock, numberStyles);
return startBlock;
} else {
return -1;
}
}
int Start(int styleBase) noexcept {
const int block = BlockFromBaseStyle(styleBase);
return (block >= 0) ? classifiers[block].Start() : -1;
}
int Length(int styleBase) noexcept {
const int block = BlockFromBaseStyle(styleBase);
return (block >= 0) ? classifiers[block].Length() : 0;
}
int BaseStyle(int subStyle) const noexcept {
const int block = BlockFromStyle(subStyle);
if (block >= 0)
return classifiers[block].Base();
else
return subStyle;
}
int DistanceToSecondaryStyles() const noexcept {
return secondaryDistance;
}
int FirstAllocated() const noexcept {
int start = 257;
for (const WordClassifier &wc : classifiers) {
if ((wc.Length() > 0) && (start > wc.Start()))
start = wc.Start();
}
return (start < 256) ? start : -1;
}
int LastAllocated() const noexcept {
int last = -1;
for (const WordClassifier &wc : classifiers) {
if ((wc.Length() > 0) && (last < wc.Last()))
last = wc.Last();
}
return last;
}
void SetIdentifiers(int style, const char *identifiers, bool lowerCase=false) {
const int block = BlockFromStyle(style);
if (block >= 0)
classifiers[block].SetIdentifiers(style, identifiers, lowerCase);
}
void Free() noexcept {
allocated = 0;
for (WordClassifier &wc : classifiers) {
wc.Clear();
}
}
const WordClassifier &Classifier(int baseStyle) const noexcept {
const int block = BlockFromBaseStyle(baseStyle);
return classifiers[block >= 0 ? block : 0];
}
};
}
#endif

View File

@ -0,0 +1,315 @@
// Scintilla source code edit control
/** @file WordList.cxx
** Hold a list of words.
**/
// Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstdlib>
#include <cassert>
#include <cstring>
#include <string>
#include <algorithm>
#include <iterator>
#include <memory>
#include "WordList.h"
#include "CharacterSet.h"
using namespace Lexilla;
namespace {
/**
* Creates an array that points into each word in the string and puts \0 terminators
* after each word.
*/
std::unique_ptr<char *[]> ArrayFromWordList(char *wordlist, size_t slen, size_t *len, bool onlyLineEnds = false) {
assert(wordlist);
size_t words = 0;
// For rapid determination of whether a character is a separator, build
// a look up table.
bool wordSeparator[256] = {}; // Initialise all to false.
wordSeparator[static_cast<unsigned int>('\r')] = true;
wordSeparator[static_cast<unsigned int>('\n')] = true;
if (!onlyLineEnds) {
wordSeparator[static_cast<unsigned int>(' ')] = true;
wordSeparator[static_cast<unsigned int>('\t')] = true;
}
unsigned char prev = '\n';
for (int j = 0; wordlist[j]; j++) {
const unsigned char curr = wordlist[j];
if (!wordSeparator[curr] && wordSeparator[prev])
words++;
prev = curr;
}
std::unique_ptr<char *[]> keywords = std::make_unique<char *[]>(words + 1);
size_t wordsStore = 0;
if (words) {
unsigned char previous = '\0';
for (size_t k = 0; k < slen; k++) {
if (!wordSeparator[static_cast<unsigned char>(wordlist[k])]) {
if (!previous) {
keywords[wordsStore] = &wordlist[k];
wordsStore++;
}
} else {
wordlist[k] = '\0';
}
previous = wordlist[k];
}
}
assert(wordsStore < (words + 1));
keywords[wordsStore] = &wordlist[slen];
*len = wordsStore;
return keywords;
}
bool cmpWords(const char *a, const char *b) noexcept {
return strcmp(a, b) < 0;
}
}
WordList::WordList(bool onlyLineEnds_) noexcept :
words(nullptr), list(nullptr), len(0), onlyLineEnds(onlyLineEnds_) {
// Prevent warnings by static analyzers about uninitialized starts.
starts[0] = -1;
}
WordList::~WordList() {
Clear();
}
WordList::operator bool() const noexcept {
return len != 0;
}
bool WordList::operator!=(const WordList &other) const noexcept {
if (len != other.len)
return true;
for (size_t i=0; i<len; i++) {
if (strcmp(words[i], other.words[i]) != 0)
return true;
}
return false;
}
int WordList::Length() const noexcept {
return static_cast<int>(len);
}
void WordList::Clear() noexcept {
delete []list;
list = nullptr;
delete []words;
words = nullptr;
len = 0;
}
bool WordList::Set(const char *s, bool lowerCase) {
const size_t lenS = strlen(s) + 1;
std::unique_ptr<char[]> listTemp = std::make_unique<char[]>(lenS);
memcpy(listTemp.get(), s, lenS);
if (lowerCase) {
for (size_t i = 0; i < lenS; i++) {
listTemp[i] = MakeLowerCase(listTemp[i]);
}
}
size_t lenTemp = 0;
std::unique_ptr<char *[]> wordsTemp = ArrayFromWordList(listTemp.get(), lenS - 1, &lenTemp, onlyLineEnds);
std::sort(wordsTemp.get(), wordsTemp.get() + lenTemp, cmpWords);
if (lenTemp == len) {
bool changed = false;
for (size_t i = 0; i < lenTemp; i++) {
if (strcmp(words[i], wordsTemp[i]) != 0) {
changed = true;
break;
}
}
if (!changed) {
return false;
}
}
Clear();
words = wordsTemp.release();
list = listTemp.release();
len = lenTemp;
std::fill(starts, std::end(starts), -1);
for (int l = static_cast<int>(len - 1); l >= 0; l--) {
unsigned char const indexChar = words[l][0];
starts[indexChar] = l;
}
return true;
}
/** Check whether a string is in the list.
* List elements are either exact matches or prefixes.
* Prefix elements start with '^' and match all strings that start with the rest of the element
* so '^GTK_' matches 'GTK_X', 'GTK_MAJOR_VERSION', and 'GTK_'.
*/
bool WordList::InList(const char *s) const noexcept {
if (!words)
return false;
const char first = s[0];
const unsigned char firstChar = first;
int j = starts[firstChar];
if (j >= 0) {
while (words[j][0] == first) {
if (s[1] == words[j][1]) {
const char *a = words[j] + 1;
const char *b = s + 1;
while (*a && *a == *b) {
a++;
b++;
}
if (!*a && !*b)
return true;
}
j++;
}
}
j = starts[static_cast<unsigned int>('^')];
if (j >= 0) {
while (words[j][0] == '^') {
const char *a = words[j] + 1;
const char *b = s;
while (*a && *a == *b) {
a++;
b++;
}
if (!*a)
return true;
j++;
}
}
return false;
}
/** convenience overload so can easily call with std::string.
*/
bool WordList::InList(const std::string &s) const noexcept {
return InList(s.c_str());
}
/** similar to InList, but word s can be a substring of keyword.
* eg. the keyword define is defined as def~ine. This means the word must start
* with def to be a keyword, but also defi, defin and define are valid.
* The marker is ~ in this case.
*/
bool WordList::InListAbbreviated(const char *s, const char marker) const noexcept {
if (!words)
return false;
const char first = s[0];
const unsigned char firstChar = first;
int j = starts[firstChar];
if (j >= 0) {
while (words[j][0] == first) {
bool isSubword = false;
int start = 1;
if (words[j][1] == marker) {
isSubword = true;
start++;
}
if (s[1] == words[j][start]) {
const char *a = words[j] + start;
const char *b = s + 1;
while (*a && *a == *b) {
a++;
if (*a == marker) {
isSubword = true;
a++;
}
b++;
}
if ((!*a || isSubword) && !*b)
return true;
}
j++;
}
}
j = starts[static_cast<unsigned int>('^')];
if (j >= 0) {
while (words[j][0] == '^') {
const char *a = words[j] + 1;
const char *b = s;
while (*a && *a == *b) {
a++;
b++;
}
if (!*a)
return true;
j++;
}
}
return false;
}
/** similar to InListAbbreviated, but word s can be an abridged version of a keyword.
* eg. the keyword is defined as "after.~:". This means the word must have a prefix (begins with) of
* "after." and suffix (ends with) of ":" to be a keyword, Hence "after.field:" , "after.form.item:" are valid.
* Similarly "~.is.valid" keyword is suffix only... hence "field.is.valid" , "form.is.valid" are valid.
* The marker is ~ in this case.
* No multiple markers check is done and wont work.
*/
bool WordList::InListAbridged(const char *s, const char marker) const noexcept {
if (!words)
return false;
const char first = s[0];
const unsigned char firstChar = first;
int j = starts[firstChar];
if (j >= 0) {
while (words[j][0] == first) {
const char *a = words[j];
const char *b = s;
while (*a && *a == *b) {
a++;
if (*a == marker) {
a++;
const size_t suffixLengthA = strlen(a);
const size_t suffixLengthB = strlen(b);
if (suffixLengthA >= suffixLengthB)
break;
b = b + suffixLengthB - suffixLengthA - 1;
}
b++;
}
if (!*a && !*b)
return true;
j++;
}
}
j = starts[static_cast<unsigned int>(marker)];
if (j >= 0) {
while (words[j][0] == marker) {
const char *a = words[j] + 1;
const char *b = s;
const size_t suffixLengthA = strlen(a);
const size_t suffixLengthB = strlen(b);
if (suffixLengthA > suffixLengthB) {
j++;
continue;
}
b = b + suffixLengthB - suffixLengthA;
while (*a && *a == *b) {
a++;
b++;
}
if (!*a && !*b)
return true;
j++;
}
}
return false;
}
const char *WordList::WordAt(int n) const noexcept {
return words[n];
}

View File

@ -0,0 +1,44 @@
// Scintilla source code edit control
/** @file WordList.h
** Hold a list of words.
**/
// Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef WORDLIST_H
#define WORDLIST_H
namespace Lexilla {
/**
*/
class WordList {
// Each word contains at least one character - an empty word acts as sentinel at the end.
char **words;
char *list;
size_t len;
bool onlyLineEnds; ///< Delimited by any white space or only line ends
int starts[256];
public:
explicit WordList(bool onlyLineEnds_ = false) noexcept;
// Deleted so WordList objects can not be copied.
WordList(const WordList &) = delete;
WordList(WordList &&) = delete;
WordList &operator=(const WordList &) = delete;
WordList &operator=(WordList &&) = delete;
~WordList();
operator bool() const noexcept;
bool operator!=(const WordList &other) const noexcept;
int Length() const noexcept;
void Clear() noexcept;
bool Set(const char *s, bool lowerCase=false);
bool InList(const char *s) const noexcept;
bool InList(const std::string &s) const noexcept;
bool InListAbbreviated(const char *s, const char marker) const noexcept;
bool InListAbridged(const char *s, const char marker) const noexcept;
const char *WordAt(int n) const noexcept;
};
}
#endif