summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTimothy Pearson <kb9vqf@pearsoncomputing.net>2011-11-30 11:36:13 -0600
committerTimothy Pearson <kb9vqf@pearsoncomputing.net>2011-11-30 11:36:13 -0600
commit664e37abfe5c796c1279b8295fb030f126b0a7d8 (patch)
tree85f4e661e5c615f01ee1cdf51ca1250b96efe315 /src
downloadtqscintilla-664e37abfe5c796c1279b8295fb030f126b0a7d8.tar.gz
tqscintilla-664e37abfe5c796c1279b8295fb030f126b0a7d8.zip
Initial import of qscintilla from 2007
Diffstat (limited to 'src')
-rwxr-xr-xsrc/AutoComplete.cpp174
-rwxr-xr-xsrc/AutoComplete.h70
-rwxr-xr-xsrc/CallTip.cpp314
-rwxr-xr-xsrc/CallTip.h79
-rwxr-xr-xsrc/CellBuffer.cpp1120
-rwxr-xr-xsrc/CellBuffer.h250
-rw-r--r--src/CharClassify.cpp43
-rw-r--r--src/CharClassify.h25
-rwxr-xr-xsrc/ContractionState.cpp289
-rwxr-xr-xsrc/ContractionState.h65
-rwxr-xr-xsrc/Document.cpp1577
-rwxr-xr-xsrc/Document.h305
-rwxr-xr-xsrc/DocumentAccessor.cpp187
-rwxr-xr-xsrc/DocumentAccessor.h67
-rwxr-xr-xsrc/Editor.cpp7299
-rwxr-xr-xsrc/Editor.h582
-rwxr-xr-xsrc/ExternalLexer.cpp259
-rw-r--r--src/ExternalLexer.h102
-rwxr-xr-xsrc/Indicator.cpp77
-rwxr-xr-xsrc/Indicator.h22
-rwxr-xr-xsrc/KeyMap.cpp148
-rwxr-xr-xsrc/KeyMap.h43
-rwxr-xr-xsrc/KeyWords.cpp221
-rwxr-xr-xsrc/LexAPDL.cpp136
-rwxr-xr-xsrc/LexAU3.cpp891
-rwxr-xr-xsrc/LexAVE.cpp225
-rwxr-xr-xsrc/LexAda.cpp520
-rwxr-xr-xsrc/LexAsm.cpp177
-rwxr-xr-xsrc/LexAsn1.cpp181
-rwxr-xr-xsrc/LexBaan.cpp189
-rwxr-xr-xsrc/LexBash.cpp663
-rw-r--r--src/LexBasic.cpp369
-rwxr-xr-xsrc/LexBullant.cpp225
-rwxr-xr-xsrc/LexCLW.cpp675
-rwxr-xr-xsrc/LexCPP.cpp489
-rwxr-xr-xsrc/LexCSS.cpp303
-rw-r--r--src/LexCaml.cpp399
-rwxr-xr-xsrc/LexConf.cpp184
-rwxr-xr-xsrc/LexCrontab.cpp218
-rw-r--r--src/LexCsound.cpp207
-rwxr-xr-xsrc/LexEScript.cpp270
-rwxr-xr-xsrc/LexEiffel.cpp234
-rwxr-xr-xsrc/LexErlang.cpp522
-rw-r--r--src/LexFlagship.cpp226
-rwxr-xr-xsrc/LexForth.cpp348
-rwxr-xr-xsrc/LexFortran.cpp452
-rwxr-xr-xsrc/LexGen.py241
-rwxr-xr-xsrc/LexGui4Cli.cpp309
-rwxr-xr-xsrc/LexHTML.cpp2042
-rw-r--r--src/LexHaskell.cpp263
-rw-r--r--src/LexInno.cpp290
-rwxr-xr-xsrc/LexKix.cpp122
-rwxr-xr-xsrc/LexLisp.cpp275
-rwxr-xr-xsrc/LexLout.cpp208
-rwxr-xr-xsrc/LexLua.cpp357
-rwxr-xr-xsrc/LexMMIXAL.cpp183
-rwxr-xr-xsrc/LexMPT.cpp182
-rwxr-xr-xsrc/LexMSSQL.cpp359
-rwxr-xr-xsrc/LexMatlab.cpp225
-rwxr-xr-xsrc/LexMetapost.cpp320
-rwxr-xr-xsrc/LexNsis.cpp647
-rw-r--r--src/LexOpal.cpp518
-rwxr-xr-xsrc/LexOthers.cpp1140
-rwxr-xr-xsrc/LexPB.cpp358
-rwxr-xr-xsrc/LexPOV.cpp312
-rwxr-xr-xsrc/LexPS.cpp343
-rwxr-xr-xsrc/LexPascal.cpp369
-rwxr-xr-xsrc/LexPerl.cpp1256
-rwxr-xr-xsrc/LexPython.cpp449
-rw-r--r--src/LexRebol.cpp319
-rwxr-xr-xsrc/LexRuby.cpp1542
-rwxr-xr-xsrc/LexSQL.cpp342
-rwxr-xr-xsrc/LexScriptol.cpp404
-rw-r--r--src/LexSmalltalk.cpp317
-rwxr-xr-xsrc/LexSpecman.cpp286
-rw-r--r--src/LexSpice.cpp221
-rw-r--r--src/LexTADS3.cpp837
-rw-r--r--src/LexTCL.cpp362
-rwxr-xr-xsrc/LexTeX.cpp288
-rwxr-xr-xsrc/LexVB.cpp278
-rwxr-xr-xsrc/LexVHDL.cpp473
-rwxr-xr-xsrc/LexVerilog.cpp299
-rwxr-xr-xsrc/LexYAML.cpp305
-rwxr-xr-xsrc/License.txt20
-rwxr-xr-xsrc/LineMarker.cpp301
-rwxr-xr-xsrc/LineMarker.h54
-rwxr-xr-xsrc/PropSet.cpp1170
-rwxr-xr-xsrc/RESearch.cpp788
-rwxr-xr-xsrc/RESearch.h65
-rwxr-xr-xsrc/SVector.h127
-rwxr-xr-xsrc/SciTE.properties6
-rwxr-xr-xsrc/ScintillaBase.cpp727
-rwxr-xr-xsrc/ScintillaBase.h93
-rwxr-xr-xsrc/Style.cpp154
-rwxr-xr-xsrc/Style.h56
-rwxr-xr-xsrc/StyleContext.cpp51
-rwxr-xr-xsrc/StyleContext.h169
-rwxr-xr-xsrc/UniConversion.cpp76
-rwxr-xr-xsrc/UniConversion.h12
-rwxr-xr-xsrc/ViewStyle.cpp297
-rwxr-xr-xsrc/ViewStyle.h108
-rwxr-xr-xsrc/WindowAccessor.cpp178
-rw-r--r--src/XPM.cpp348
-rw-r--r--src/XPM.h94
104 files changed, 43356 insertions, 0 deletions
diff --git a/src/AutoComplete.cpp b/src/AutoComplete.cpp
new file mode 100755
index 0000000..753adca
--- /dev/null
+++ b/src/AutoComplete.cpp
@@ -0,0 +1,174 @@
+// Scintilla source code edit control
+/** @file AutoComplete.cxx
+ ** Defines the auto completion list box.
+ **/
+// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "AutoComplete.h"
+
+AutoComplete::AutoComplete() :
+ active(false),
+ separator(' '),
+ typesep('?'),
+ ignoreCase(false),
+ chooseSingle(false),
+ lb(0),
+ posStart(0),
+ startLen(0),
+ cancelAtStartPos(true),
+ autoHide(true),
+ dropRestOfWord(false) {
+ lb = ListBox::Allocate();
+ stopChars[0] = '\0';
+ fillUpChars[0] = '\0';
+}
+
+AutoComplete::~AutoComplete() {
+ if (lb) {
+ lb->Destroy();
+ delete lb;
+ lb = 0;
+ }
+}
+
+bool AutoComplete::Active() {
+ return active;
+}
+
+void AutoComplete::Start(Window &parent, int ctrlID,
+ int position, Point location, int startLen_,
+ int lineHeight, bool unicodeMode) {
+ if (active) {
+ Cancel();
+ }
+ lb->Create(parent, ctrlID, location, lineHeight, unicodeMode);
+ lb->Clear();
+ active = true;
+ startLen = startLen_;
+ posStart = position;
+}
+
+void AutoComplete::SetStopChars(const char *stopChars_) {
+ strncpy(stopChars, stopChars_, sizeof(stopChars));
+ stopChars[sizeof(stopChars) - 1] = '\0';
+}
+
+bool AutoComplete::IsStopChar(char ch) {
+ return ch && strchr(stopChars, ch);
+}
+
+void AutoComplete::SetFillUpChars(const char *fillUpChars_) {
+ strncpy(fillUpChars, fillUpChars_, sizeof(fillUpChars));
+ fillUpChars[sizeof(fillUpChars) - 1] = '\0';
+}
+
+bool AutoComplete::IsFillUpChar(char ch) {
+ return ch && strchr(fillUpChars, ch);
+}
+
+void AutoComplete::SetSeparator(char separator_) {
+ separator = separator_;
+}
+
+char AutoComplete::GetSeparator() {
+ return separator;
+}
+
+void AutoComplete::SetTypesep(char separator_) {
+ typesep = separator_;
+}
+
+char AutoComplete::GetTypesep() {
+ return typesep;
+}
+
+void AutoComplete::SetList(const char *list) {
+ lb->SetList(list, separator, typesep);
+}
+
+void AutoComplete::Show(bool show) {
+ lb->Show(show);
+ if (show)
+ lb->Select(0);
+}
+
+void AutoComplete::Cancel() {
+ if (lb->Created()) {
+ lb->Clear();
+ lb->Destroy();
+ active = false;
+ }
+}
+
+
+void AutoComplete::Move(int delta) {
+ int count = lb->Length();
+ int current = lb->GetSelection();
+ current += delta;
+ if (current >= count)
+ current = count - 1;
+ if (current < 0)
+ current = 0;
+ lb->Select(current);
+}
+
+void AutoComplete::Select(const char *word) {
+ size_t lenWord = strlen(word);
+ int location = -1;
+ const int maxItemLen=1000;
+ char item[maxItemLen];
+ int start = 0; // lower bound of the api array block to search
+ int end = lb->Length() - 1; // upper bound of the api array block to search
+ while ((start <= end) && (location == -1)) { // Binary searching loop
+ int pivot = (start + end) / 2;
+ lb->GetValue(pivot, item, maxItemLen);
+ int cond;
+ if (ignoreCase)
+ cond = CompareNCaseInsensitive(word, item, lenWord);
+ else
+ cond = strncmp(word, item, lenWord);
+ if (!cond) {
+ // Find first match
+ while (pivot > start) {
+ lb->GetValue(pivot-1, item, maxItemLen);
+ if (ignoreCase)
+ cond = CompareNCaseInsensitive(word, item, lenWord);
+ else
+ cond = strncmp(word, item, lenWord);
+ if (0 != cond)
+ break;
+ --pivot;
+ }
+ location = pivot;
+ if (ignoreCase) {
+ // Check for exact-case match
+ for (; pivot <= end; pivot++) {
+ lb->GetValue(pivot, item, maxItemLen);
+ if (!strncmp(word, item, lenWord)) {
+ location = pivot;
+ break;
+ }
+ if (CompareNCaseInsensitive(word, item, lenWord))
+ break;
+ }
+ }
+ } else if (cond < 0) {
+ end = pivot - 1;
+ } else if (cond > 0) {
+ start = pivot + 1;
+ }
+ }
+ if (location == -1 && autoHide)
+ Cancel();
+ else
+ lb->Select(location);
+}
+
diff --git a/src/AutoComplete.h b/src/AutoComplete.h
new file mode 100755
index 0000000..10577ca
--- /dev/null
+++ b/src/AutoComplete.h
@@ -0,0 +1,70 @@
+// Scintilla source code edit control
+/** @file AutoComplete.h
+ ** Defines the auto completion list box.
+ **/
+// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef AUTOCOMPLETE_H
+#define AUTOCOMPLETE_H
+
+/**
+ */
+class AutoComplete {
+ bool active;
+ char stopChars[256];
+ char fillUpChars[256];
+ char separator;
+ char typesep; // Type seperator
+
+public:
+ bool ignoreCase;
+ bool chooseSingle;
+ ListBox *lb;
+ int posStart;
+ int startLen;
+ /// Should autocompletion be canceled if editor's currentPos <= startPos?
+ bool cancelAtStartPos;
+ bool autoHide;
+ bool dropRestOfWord;
+
+ AutoComplete();
+ ~AutoComplete();
+
+ /// Is the auto completion list displayed?
+ bool Active();
+
+ /// Display the auto completion list positioned to be near a character position
+ void Start(Window &parent, int ctrlID, int position, Point location,
+ int startLen_, int lineHeight, bool unicodeMode);
+
+ /// The stop chars are characters which, when typed, cause the auto completion list to disappear
+ void SetStopChars(const char *stopChars_);
+ bool IsStopChar(char ch);
+
+ /// The fillup chars are characters which, when typed, fill up the selected word
+ void SetFillUpChars(const char *fillUpChars_);
+ bool IsFillUpChar(char ch);
+
+ /// The separator character is used when interpreting the list in SetList
+ void SetSeparator(char separator_);
+ char GetSeparator();
+
+ /// The typesep character is used for seperating the word from the type
+ void SetTypesep(char separator_);
+ char GetTypesep();
+
+ /// The list string contains a sequence of words separated by the separator character
+ void SetList(const char *list);
+
+ void Show(bool show);
+ void Cancel();
+
+ /// Move the current list element by delta, scrolling appropriately
+ void Move(int delta);
+
+ /// Select a list element that starts with word as the current element
+ void Select(const char *word);
+};
+
+#endif
diff --git a/src/CallTip.cpp b/src/CallTip.cpp
new file mode 100755
index 0000000..f4bc5f8
--- /dev/null
+++ b/src/CallTip.cpp
@@ -0,0 +1,314 @@
+// Scintilla source code edit control
+/** @file CallTip.cxx
+ ** Code for displaying call tips.
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "Platform.h"
+
+#include "Scintilla.h"
+#include "CallTip.h"
+
+static const int insetX = 5; // text inset in x from calltip border
+static const int widthArrow = 14;
+
+CallTip::CallTip() {
+ wCallTip = 0;
+ inCallTipMode = false;
+ posStartCallTip = 0;
+ val = 0;
+ rectUp = PRectangle(0,0,0,0);
+ rectDown = PRectangle(0,0,0,0);
+ lineHeight = 1;
+ startHighlight = 0;
+ endHighlight = 0;
+ tabSize = 0;
+ useStyleCallTip = false; // for backwards compatibility
+
+ colourBG.desired = ColourDesired(0xff, 0xff, 0xff);
+ colourUnSel.desired = ColourDesired(0x80, 0x80, 0x80);
+ colourSel.desired = ColourDesired(0, 0, 0x80);
+ colourShade.desired = ColourDesired(0, 0, 0);
+ colourLight.desired = ColourDesired(0xc0, 0xc0, 0xc0);
+}
+
+CallTip::~CallTip() {
+ font.Release();
+ wCallTip.Destroy();
+ delete []val;
+ val = 0;
+}
+
+void CallTip::RefreshColourPalette(Palette &pal, bool want) {
+ pal.WantFind(colourBG, want);
+ pal.WantFind(colourUnSel, want);
+ pal.WantFind(colourSel, want);
+ pal.WantFind(colourShade, want);
+ pal.WantFind(colourLight, want);
+}
+
+// Although this test includes 0, we should never see a \0 character.
+static bool IsArrowCharacter(char ch) {
+ return (ch == 0) || (ch == '\001') || (ch == '\002');
+}
+
+// We ignore tabs unless a tab width has been set.
+bool CallTip::IsTabCharacter(char ch) {
+ return (tabSize > 0) && (ch == '\t');
+}
+
+int CallTip::NextTabPos(int x) {
+ if (tabSize > 0) { // paranoia... not called unless this is true
+ x -= insetX; // position relative to text
+ x = (x + tabSize) / tabSize; // tab "number"
+ return tabSize*x + insetX; // position of next tab
+ } else {
+ return x + 1; // arbitrary
+ }
+}
+
+// Draw a section of the call tip that does not include \n in one colour.
+// The text may include up to numEnds tabs or arrow characters.
+void CallTip::DrawChunk(Surface *surface, int &x, const char *s,
+ int posStart, int posEnd, int ytext, PRectangle rcClient,
+ bool highlight, bool draw) {
+ s += posStart;
+ int len = posEnd - posStart;
+
+ // Divide the text into sections that are all text, or that are
+ // single arrows or single tab characters (if tabSize > 0).
+ int maxEnd = 0;
+ const int numEnds = 10;
+ int ends[numEnds + 2];
+ for (int i=0;i<len;i++) {
+ if ((maxEnd < numEnds) &&
+ (IsArrowCharacter(s[i]) || IsTabCharacter(s[i])) ) {
+ if (i > 0)
+ ends[maxEnd++] = i;
+ ends[maxEnd++] = i+1;
+ }
+ }
+ ends[maxEnd++] = len;
+ int startSeg = 0;
+ int xEnd;
+ for (int seg = 0; seg<maxEnd; seg++) {
+ int endSeg = ends[seg];
+ if (endSeg > startSeg) {
+ if (IsArrowCharacter(s[startSeg])) {
+ bool upArrow = s[startSeg] == '\001';
+ rcClient.left = x;
+ rcClient.right = rcClient.left + widthArrow;
+ if (draw) {
+ const int halfWidth = widthArrow / 2 - 3;
+ const int centreX = rcClient.left + widthArrow / 2 - 1;
+ const int centreY = (rcClient.top + rcClient.bottom) / 2;
+ surface->FillRectangle(rcClient, colourBG.allocated);
+ PRectangle rcClientInner(rcClient.left + 1, rcClient.top + 1,
+ rcClient.right - 2, rcClient.bottom - 1);
+ surface->FillRectangle(rcClientInner, colourUnSel.allocated);
+
+ if (upArrow) { // Up arrow
+ Point pts[] = {
+ Point(centreX - halfWidth, centreY + halfWidth / 2),
+ Point(centreX + halfWidth, centreY + halfWidth / 2),
+ Point(centreX, centreY - halfWidth + halfWidth / 2),
+ };
+ surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
+ colourBG.allocated, colourBG.allocated);
+ } else { // Down arrow
+ Point pts[] = {
+ Point(centreX - halfWidth, centreY - halfWidth / 2),
+ Point(centreX + halfWidth, centreY - halfWidth / 2),
+ Point(centreX, centreY + halfWidth - halfWidth / 2),
+ };
+ surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
+ colourBG.allocated, colourBG.allocated);
+ }
+ }
+ xEnd = rcClient.right;
+ offsetMain = xEnd;
+ if (upArrow) {
+ rectUp = rcClient;
+ } else {
+ rectDown = rcClient;
+ }
+ } else if (IsTabCharacter(s[startSeg])) {
+ xEnd = NextTabPos(x);
+ } else {
+ xEnd = x + surface->WidthText(font, s + startSeg, endSeg - startSeg);
+ if (draw) {
+ rcClient.left = x;
+ rcClient.right = xEnd;
+ surface->DrawTextTransparent(rcClient, font, ytext,
+ s+startSeg, endSeg - startSeg,
+ highlight ? colourSel.allocated : colourUnSel.allocated);
+ }
+ }
+ x = xEnd;
+ startSeg = endSeg;
+ }
+ }
+}
+
+int CallTip::PaintContents(Surface *surfaceWindow, bool draw) {
+ PRectangle rcClientPos = wCallTip.GetClientPosition();
+ PRectangle rcClientSize(0, 0, rcClientPos.right - rcClientPos.left,
+ rcClientPos.bottom - rcClientPos.top);
+ PRectangle rcClient(1, 1, rcClientSize.right - 1, rcClientSize.bottom - 1);
+
+ // To make a nice small call tip window, it is only sized to fit most normal characters without accents
+ int ascent = surfaceWindow->Ascent(font) - surfaceWindow->InternalLeading(font);
+
+ // For each line...
+ // Draw the definition in three parts: before highlight, highlighted, after highlight
+ int ytext = rcClient.top + ascent + 1;
+ rcClient.bottom = ytext + surfaceWindow->Descent(font) + 1;
+ char *chunkVal = val;
+ bool moreChunks = true;
+ int maxWidth = 0;
+ while (moreChunks) {
+ char *chunkEnd = strchr(chunkVal, '\n');
+ if (chunkEnd == NULL) {
+ chunkEnd = chunkVal + strlen(chunkVal);
+ moreChunks = false;
+ }
+ int chunkOffset = chunkVal - val;
+ int chunkLength = chunkEnd - chunkVal;
+ int chunkEndOffset = chunkOffset + chunkLength;
+ int thisStartHighlight = Platform::Maximum(startHighlight, chunkOffset);
+ thisStartHighlight = Platform::Minimum(thisStartHighlight, chunkEndOffset);
+ thisStartHighlight -= chunkOffset;
+ int thisEndHighlight = Platform::Maximum(endHighlight, chunkOffset);
+ thisEndHighlight = Platform::Minimum(thisEndHighlight, chunkEndOffset);
+ thisEndHighlight -= chunkOffset;
+ rcClient.top = ytext - ascent - 1;
+
+ int x = insetX; // start each line at this inset
+
+ DrawChunk(surfaceWindow, x, chunkVal, 0, thisStartHighlight,
+ ytext, rcClient, false, draw);
+ DrawChunk(surfaceWindow, x, chunkVal, thisStartHighlight, thisEndHighlight,
+ ytext, rcClient, true, draw);
+ DrawChunk(surfaceWindow, x, chunkVal, thisEndHighlight, chunkLength,
+ ytext, rcClient, false, draw);
+
+ chunkVal = chunkEnd + 1;
+ ytext += lineHeight;
+ rcClient.bottom += lineHeight;
+ maxWidth = Platform::Maximum(maxWidth, x);
+ }
+ return maxWidth;
+}
+
+void CallTip::PaintCT(Surface *surfaceWindow) {
+ if (!val)
+ return;
+ PRectangle rcClientPos = wCallTip.GetClientPosition();
+ PRectangle rcClientSize(0, 0, rcClientPos.right - rcClientPos.left,
+ rcClientPos.bottom - rcClientPos.top);
+ PRectangle rcClient(1, 1, rcClientSize.right - 1, rcClientSize.bottom - 1);
+
+ surfaceWindow->FillRectangle(rcClient, colourBG.allocated);
+
+ offsetMain = insetX; // initial alignment assuming no arrows
+ PaintContents(surfaceWindow, true);
+
+ // Draw a raised border around the edges of the window
+ surfaceWindow->MoveTo(0, rcClientSize.bottom - 1);
+ surfaceWindow->PenColour(colourShade.allocated);
+ surfaceWindow->LineTo(rcClientSize.right - 1, rcClientSize.bottom - 1);
+ surfaceWindow->LineTo(rcClientSize.right - 1, 0);
+ surfaceWindow->PenColour(colourLight.allocated);
+ surfaceWindow->LineTo(0, 0);
+ surfaceWindow->LineTo(0, rcClientSize.bottom - 1);
+}
+
+void CallTip::MouseClick(Point pt) {
+ clickPlace = 0;
+ if (rectUp.Contains(pt))
+ clickPlace = 1;
+ if (rectDown.Contains(pt))
+ clickPlace = 2;
+}
+
+PRectangle CallTip::CallTipStart(int pos, Point pt, const char *defn,
+ const char *faceName, int size,
+ int codePage_, int characterSet, Window &wParent) {
+ clickPlace = 0;
+ if (val)
+ delete []val;
+ val = new char[strlen(defn) + 1];
+ if (!val)
+ return PRectangle();
+ strcpy(val, defn);
+ codePage = codePage_;
+ Surface *surfaceMeasure = Surface::Allocate();
+ if (!surfaceMeasure)
+ return PRectangle();
+ surfaceMeasure->Init(wParent.GetID());
+ surfaceMeasure->SetUnicodeMode(SC_CP_UTF8 == codePage);
+ surfaceMeasure->SetDBCSMode(codePage);
+ startHighlight = 0;
+ endHighlight = 0;
+ inCallTipMode = true;
+ posStartCallTip = pos;
+ int deviceHeight = surfaceMeasure->DeviceHeightFont(size);
+ font.Create(faceName, characterSet, deviceHeight, false, false);
+ // Look for multiple lines in the text
+ // Only support \n here - simply means container must avoid \r!
+ int numLines = 1;
+ const char *newline;
+ const char *look = val;
+ rectUp = PRectangle(0,0,0,0);
+ rectDown = PRectangle(0,0,0,0);
+ offsetMain = insetX; // changed to right edge of any arrows
+ int width = PaintContents(surfaceMeasure, false) + insetX;
+ while ((newline = strchr(look, '\n')) != NULL) {
+ look = newline + 1;
+ numLines++;
+ }
+ lineHeight = surfaceMeasure->Height(font);
+
+ // Extra line for border and an empty line at top and bottom. The returned
+ // rectangle is aligned to the right edge of the last arrow encountered in
+ // the tip text, else to the tip text left edge.
+ int height = lineHeight * numLines - surfaceMeasure->InternalLeading(font) + 2 + 2;
+ delete surfaceMeasure;
+ return PRectangle(pt.x - offsetMain, pt.y + 1, pt.x + width - offsetMain, pt.y + 1 + height);
+}
+
+void CallTip::CallTipCancel() {
+ inCallTipMode = false;
+ if (wCallTip.Created()) {
+ wCallTip.Destroy();
+ }
+}
+
+void CallTip::SetHighlight(int start, int end) {
+ // Avoid flashing by checking something has really changed
+ if ((start != startHighlight) || (end != endHighlight)) {
+ startHighlight = start;
+ endHighlight = end;
+ if (wCallTip.Created()) {
+ wCallTip.InvalidateAll();
+ }
+ }
+}
+
+// Set the tab size (sizes > 0 enable the use of tabs). This also enables the
+// use of the STYLE_CALLTIP.
+void CallTip::SetTabSize(int tabSz) {
+ tabSize = tabSz;
+ useStyleCallTip = true;
+}
+
+// It might be better to have two access functions for this and to use
+// them for all settings of colours.
+void CallTip::SetForeBack(const ColourPair &fore, const ColourPair &back) {
+ colourBG = back;
+ colourUnSel = fore;
+}
diff --git a/src/CallTip.h b/src/CallTip.h
new file mode 100755
index 0000000..9848a10
--- /dev/null
+++ b/src/CallTip.h
@@ -0,0 +1,79 @@
+// Scintilla source code edit control
+/** @file CallTip.h
+ ** Interface to the call tip control.
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef CALLTIP_H
+#define CALLTIP_H
+
+/**
+ */
+class CallTip {
+ int startHighlight; // character offset to start and...
+ int endHighlight; // ...end of highlighted text
+ char *val;
+ Font font;
+ PRectangle rectUp; // rectangle of last up angle in the tip
+ PRectangle rectDown; // rectangle of last down arrow in the tip
+ int lineHeight; // vertical line spacing
+ int offsetMain; // The alignment point of the call tip
+ int tabSize; // Tab size in pixels, <=0 no TAB expand
+ bool useStyleCallTip; // if true, STYLE_CALLTIP should be used
+
+ // Private so CallTip objects can not be copied
+ CallTip(const CallTip &) {}
+ CallTip &operator=(const CallTip &) { return *this; }
+ void DrawChunk(Surface *surface, int &x, const char *s,
+ int posStart, int posEnd, int ytext, PRectangle rcClient,
+ bool highlight, bool draw);
+ int PaintContents(Surface *surfaceWindow, bool draw);
+ bool IsTabCharacter(char c);
+ int NextTabPos(int x);
+
+public:
+ Window wCallTip;
+ Window wDraw;
+ bool inCallTipMode;
+ int posStartCallTip;
+ ColourPair colourBG;
+ ColourPair colourUnSel;
+ ColourPair colourSel;
+ ColourPair colourShade;
+ ColourPair colourLight;
+ int codePage;
+ int clickPlace;
+
+ CallTip();
+ ~CallTip();
+
+ /// Claim or accept palette entries for the colours required to paint a calltip.
+ void RefreshColourPalette(Palette &pal, bool want);
+
+ void PaintCT(Surface *surfaceWindow);
+
+ void MouseClick(Point pt);
+
+ /// Setup the calltip and return a rectangle of the area required.
+ PRectangle CallTipStart(int pos, Point pt, const char *defn,
+ const char *faceName, int size, int codePage_,
+ int characterSet, Window &wParent);
+
+ void CallTipCancel();
+
+ /// Set a range of characters to be displayed in a highlight style.
+ /// Commonly used to highlight the current parameter.
+ void SetHighlight(int start, int end);
+
+ /// Set the tab size in pixels for the call tip. 0 or -ve means no tab expand.
+ void SetTabSize(int tabSz);
+
+ /// Used to determine which STYLE_xxxx to use for call tip information
+ bool UseStyleCallTip() const { return useStyleCallTip;}
+
+ // Modify foreground and background colours
+ void SetForeBack(const ColourPair &fore, const ColourPair &back);
+};
+
+#endif
diff --git a/src/CellBuffer.cpp b/src/CellBuffer.cpp
new file mode 100755
index 0000000..1109a17
--- /dev/null
+++ b/src/CellBuffer.cpp
@@ -0,0 +1,1120 @@
+// Scintilla source code edit control
+/** @file CellBuffer.cxx
+ ** Manages a buffer of cells.
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "Scintilla.h"
+#include "SVector.h"
+#include "CellBuffer.h"
+
+MarkerHandleSet::MarkerHandleSet() {
+ root = 0;
+}
+
+MarkerHandleSet::~MarkerHandleSet() {
+ MarkerHandleNumber *mhn = root;
+ while (mhn) {
+ MarkerHandleNumber *mhnToFree = mhn;
+ mhn = mhn->next;
+ delete mhnToFree;
+ }
+ root = 0;
+}
+
+int MarkerHandleSet::Length() {
+ int c = 0;
+ MarkerHandleNumber *mhn = root;
+ while (mhn) {
+ c++;
+ mhn = mhn->next;
+ }
+ return c;
+}
+
+int MarkerHandleSet::NumberFromHandle(int handle) {
+ MarkerHandleNumber *mhn = root;
+ while (mhn) {
+ if (mhn->handle == handle) {
+ return mhn->number;
+ }
+ mhn = mhn->next;
+ }
+ return - 1;
+}
+
+int MarkerHandleSet::MarkValue() {
+ unsigned int m = 0;
+ MarkerHandleNumber *mhn = root;
+ while (mhn) {
+ m |= (1 << mhn->number);
+ mhn = mhn->next;
+ }
+ return m;
+}
+
+bool MarkerHandleSet::Contains(int handle) {
+ MarkerHandleNumber *mhn = root;
+ while (mhn) {
+ if (mhn->handle == handle) {
+ return true;
+ }
+ mhn = mhn->next;
+ }
+ return false;
+}
+
+bool MarkerHandleSet::InsertHandle(int handle, int markerNum) {
+ MarkerHandleNumber *mhn = new MarkerHandleNumber;
+ if (!mhn)
+ return false;
+ mhn->handle = handle;
+ mhn->number = markerNum;
+ mhn->next = root;
+ root = mhn;
+ return true;
+}
+
+void MarkerHandleSet::RemoveHandle(int handle) {
+ MarkerHandleNumber **pmhn = &root;
+ while (*pmhn) {
+ MarkerHandleNumber *mhn = *pmhn;
+ if (mhn->handle == handle) {
+ *pmhn = mhn->next;
+ delete mhn;
+ return ;
+ }
+ pmhn = &((*pmhn)->next);
+ }
+}
+
+bool MarkerHandleSet::RemoveNumber(int markerNum) {
+ bool performedDeletion = false;
+ MarkerHandleNumber **pmhn = &root;
+ while (*pmhn) {
+ MarkerHandleNumber *mhn = *pmhn;
+ if (mhn->number == markerNum) {
+ *pmhn = mhn->next;
+ delete mhn;
+ performedDeletion = true;
+ } else {
+ pmhn = &((*pmhn)->next);
+ }
+ }
+ return performedDeletion;
+}
+
+void MarkerHandleSet::CombineWith(MarkerHandleSet *other) {
+ MarkerHandleNumber **pmhn = &root;
+ while (*pmhn) {
+ pmhn = &((*pmhn)->next);
+ }
+ *pmhn = other->root;
+ other->root = 0;
+}
+
+LineVector::LineVector() {
+ linesData = 0;
+ lines = 0;
+ size = 0;
+ levels = 0;
+ sizeLevels = 0;
+ handleCurrent = 1;
+ growSize = 1000;
+
+ Init();
+}
+
+LineVector::~LineVector() {
+ for (int line = 0; line < lines; line++) {
+ delete linesData[line].handleSet;
+ linesData[line].handleSet = 0;
+ }
+ delete []linesData;
+ linesData = 0;
+ delete []levels;
+ levels = 0;
+}
+
+void LineVector::Init() {
+ for (int line = 0; line < lines; line++) {
+ delete linesData[line].handleSet;
+ linesData[line].handleSet = 0;
+ }
+ delete []linesData;
+ linesData = new LineData[static_cast<int>(growSize)];
+ size = growSize;
+ lines = 1;
+ delete []levels;
+ levels = 0;
+ sizeLevels = 0;
+}
+
+void LineVector::Expand(int sizeNew) {
+ LineData *linesDataNew = new LineData[sizeNew];
+ if (linesDataNew) {
+ for (int i = 0; i < size; i++)
+ linesDataNew[i] = linesData[i];
+ // Do not delete handleSets here as they are transferred to new linesData
+ delete []linesData;
+ linesData = linesDataNew;
+ size = sizeNew;
+ } else {
+ Platform::DebugPrintf("No memory available\n");
+ // TODO: Blow up
+ }
+
+}
+
+void LineVector::ExpandLevels(int sizeNew) {
+ if (sizeNew == -1)
+ sizeNew = size;
+ int *levelsNew = new int[sizeNew];
+ if (levelsNew) {
+ int i = 0;
+ for (; i < sizeLevels; i++)
+ levelsNew[i] = levels[i];
+ for (; i < sizeNew; i++)
+ levelsNew[i] = SC_FOLDLEVELBASE;
+ delete []levels;
+ levels = levelsNew;
+ sizeLevels = sizeNew;
+ } else {
+ Platform::DebugPrintf("No memory available\n");
+ // TODO: Blow up
+ }
+
+}
+
+void LineVector::ClearLevels() {
+ delete []levels;
+ levels = 0;
+ sizeLevels = 0;
+}
+
+void LineVector::InsertValue(int pos, int value) {
+ //Platform::DebugPrintf("InsertValue[%d] = %d\n", pos, value);
+ if ((lines + 2) >= size) {
+ if (growSize * 6 < size)
+ growSize *= 2;
+ Expand(size + growSize);
+ if (levels) {
+ ExpandLevels(size + growSize);
+ }
+ }
+ lines++;
+ for (int i = lines; i > pos; i--) {
+ linesData[i] = linesData[i - 1];
+ }
+ linesData[pos].startPosition = value;
+ linesData[pos].handleSet = 0;
+ if (levels) {
+ for (int j = lines; j > pos; j--) {
+ levels[j] = levels[j - 1];
+ }
+ if (pos == 0) {
+ levels[pos] = SC_FOLDLEVELBASE;
+ } else if (pos == (lines - 1)) { // Last line will not be a folder
+ levels[pos] = SC_FOLDLEVELBASE;
+ } else {
+ levels[pos] = levels[pos - 1];
+ }
+ }
+}
+
+void LineVector::SetValue(int pos, int value) {
+ //Platform::DebugPrintf("SetValue[%d] = %d\n", pos, value);
+ if ((pos + 2) >= size) {
+ //Platform::DebugPrintf("Resize %d %d\n", size,pos);
+ Expand(pos + growSize);
+ //Platform::DebugPrintf("end Resize %d %d\n", size,pos);
+ lines = pos;
+ if (levels) {
+ ExpandLevels(pos + growSize);
+ }
+ }
+ linesData[pos].startPosition = value;
+}
+
+void LineVector::Remove(int pos) {
+ //Platform::DebugPrintf("Remove %d\n", pos);
+ // Retain the markers from the deleted line by oring them into the previous line
+ if (pos > 0) {
+ MergeMarkers(pos - 1);
+ }
+ for (int i = pos; i < lines; i++) {
+ linesData[i] = linesData[i + 1];
+ }
+ if (levels) {
+ // Move up following lines but merge header flag from this line
+ // to line before to avoid a temporary disappearence causing expansion.
+ int firstHeader = levels[pos] & SC_FOLDLEVELHEADERFLAG;
+ for (int j = pos; j < lines; j++) {
+ levels[j] = levels[j + 1];
+ }
+ if (pos > 0)
+ levels[pos-1] |= firstHeader;
+ }
+ lines--;
+}
+
+int LineVector::LineFromPosition(int pos) {
+ //Platform::DebugPrintf("LineFromPostion %d lines=%d end = %d\n", pos, lines, linesData[lines].startPosition);
+ if (lines == 0)
+ return 0;
+ //Platform::DebugPrintf("LineFromPosition %d\n", pos);
+ if (pos >= linesData[lines].startPosition)
+ return lines - 1;
+ int lower = 0;
+ int upper = lines;
+ do {
+ int middle = (upper + lower + 1) / 2; // Round high
+ if (pos < linesData[middle].startPosition) {
+ upper = middle - 1;
+ } else {
+ lower = middle;
+ }
+ } while (lower < upper);
+ //Platform::DebugPrintf("LineFromPostion %d %d %d\n", pos, lower, linesData[lower].startPosition, linesData[lower > 1 ? lower - 1 : 0].startPosition);
+ return lower;
+}
+
+int LineVector::AddMark(int line, int markerNum) {
+ handleCurrent++;
+ if (!linesData[line].handleSet) {
+ // Need new structure to hold marker handle
+ linesData[line].handleSet = new MarkerHandleSet;
+ if (!linesData[line].handleSet)
+ return - 1;
+ }
+ linesData[line].handleSet->InsertHandle(handleCurrent, markerNum);
+
+ return handleCurrent;
+}
+
+void LineVector::MergeMarkers(int pos) {
+ if (linesData[pos + 1].handleSet != NULL) {
+ if (linesData[pos].handleSet == NULL )
+ linesData[pos].handleSet = new MarkerHandleSet;
+ linesData[pos].handleSet->CombineWith(linesData[pos + 1].handleSet);
+ delete linesData[pos + 1].handleSet;
+ linesData[pos + 1].handleSet = NULL;
+ }
+}
+
+void LineVector::DeleteMark(int line, int markerNum, bool all) {
+ if (linesData[line].handleSet) {
+ if (markerNum == -1) {
+ delete linesData[line].handleSet;
+ linesData[line].handleSet = 0;
+ } else {
+ bool performedDeletion =
+ linesData[line].handleSet->RemoveNumber(markerNum);
+ while (all && performedDeletion) {
+ performedDeletion =
+ linesData[line].handleSet->RemoveNumber(markerNum);
+ }
+ if (linesData[line].handleSet->Length() == 0) {
+ delete linesData[line].handleSet;
+ linesData[line].handleSet = 0;
+ }
+ }
+ }
+}
+
+void LineVector::DeleteMarkFromHandle(int markerHandle) {
+ int line = LineFromHandle(markerHandle);
+ if (line >= 0) {
+ linesData[line].handleSet->RemoveHandle(markerHandle);
+ if (linesData[line].handleSet->Length() == 0) {
+ delete linesData[line].handleSet;
+ linesData[line].handleSet = 0;
+ }
+ }
+}
+
+int LineVector::LineFromHandle(int markerHandle) {
+ for (int line = 0; line < lines; line++) {
+ if (linesData[line].handleSet) {
+ if (linesData[line].handleSet->Contains(markerHandle)) {
+ return line;
+ }
+ }
+ }
+ return - 1;
+}
+
+Action::Action() {
+ at = startAction;
+ position = 0;
+ data = 0;
+ lenData = 0;
+}
+
+Action::~Action() {
+ Destroy();
+}
+
+void Action::Create(actionType at_, int position_, char *data_, int lenData_, bool mayCoalesce_) {
+ delete []data;
+ position = position_;
+ at = at_;
+ data = data_;
+ lenData = lenData_;
+ mayCoalesce = mayCoalesce_;
+}
+
+void Action::Destroy() {
+ delete []data;
+ data = 0;
+}
+
+void Action::Grab(Action *source) {
+ delete []data;
+
+ position = source->position;
+ at = source->at;
+ data = source->data;
+ lenData = source->lenData;
+ mayCoalesce = source->mayCoalesce;
+
+ // Ownership of source data transferred to this
+ source->position = 0;
+ source->at = startAction;
+ source->data = 0;
+ source->lenData = 0;
+ source->mayCoalesce = true;
+}
+
+// The undo history stores a sequence of user operations that represent the user's view of the
+// commands executed on the text.
+// Each user operation contains a sequence of text insertion and text deletion actions.
+// All the user operations are stored in a list of individual actions with 'start' actions used
+// as delimiters between user operations.
+// Initially there is one start action in the history.
+// As each action is performed, it is recorded in the history. The action may either become
+// part of the current user operation or may start a new user operation. If it is to be part of the
+// current operation, then it overwrites the current last action. If it is to be part of a new
+// operation, it is appended after the current last action.
+// After writing the new action, a new start action is appended at the end of the history.
+// The decision of whether to start a new user operation is based upon two factors. If a
+// compound operation has been explicitly started by calling BeginUndoAction and no matching
+// EndUndoAction (these calls nest) has been called, then the action is coalesced into the current
+// operation. If there is no outstanding BeginUndoAction call then a new operation is started
+// unless it looks as if the new action is caused by the user typing or deleting a stream of text.
+// Sequences that look like typing or deletion are coalesced into a single user operation.
+
+UndoHistory::UndoHistory() {
+
+ lenActions = 100;
+ actions = new Action[lenActions];
+ maxAction = 0;
+ currentAction = 0;
+ undoSequenceDepth = 0;
+ savePoint = 0;
+
+ actions[currentAction].Create(startAction);
+}
+
+UndoHistory::~UndoHistory() {
+ delete []actions;
+ actions = 0;
+}
+
+void UndoHistory::EnsureUndoRoom() {
+ // Have to test that there is room for 2 more actions in the array
+ // as two actions may be created by the calling function
+ if (currentAction >= (lenActions - 2)) {
+ // Run out of undo nodes so extend the array
+ int lenActionsNew = lenActions * 2;
+ Action *actionsNew = new Action[lenActionsNew];
+ if (!actionsNew)
+ return ;
+ for (int act = 0; act <= currentAction; act++)
+ actionsNew[act].Grab(&actions[act]);
+ delete []actions;
+ lenActions = lenActionsNew;
+ actions = actionsNew;
+ }
+}
+
+void UndoHistory::AppendAction(actionType at, int position, char *data, int lengthData) {
+ EnsureUndoRoom();
+ //Platform::DebugPrintf("%% %d action %d %d %d\n", at, position, lengthData, currentAction);
+ //Platform::DebugPrintf("^ %d action %d %d\n", actions[currentAction - 1].at,
+ // actions[currentAction - 1].position, actions[currentAction - 1].lenData);
+ if (currentAction < savePoint) {
+ savePoint = -1;
+ }
+ if (currentAction >= 1) {
+ if (0 == undoSequenceDepth) {
+ // Top level actions may not always be coalesced
+ Action &actPrevious = actions[currentAction - 1];
+ // See if current action can be coalesced into previous action
+ // Will work if both are inserts or deletes and position is same
+ if (at != actPrevious.at) {
+ currentAction++;
+ } else if (currentAction == savePoint) {
+ currentAction++;
+ } else if ((at == insertAction) &&
+ (position != (actPrevious.position + actPrevious.lenData))) {
+ // Insertions must be immediately after to coalesce
+ currentAction++;
+ } else if (!actions[currentAction].mayCoalesce) {
+ // Not allowed to coalesce if this set
+ currentAction++;
+ } else if (at == removeAction) {
+ if ((lengthData == 1) || (lengthData == 2)){
+ if ((position + lengthData) == actPrevious.position) {
+ ; // Backspace -> OK
+ } else if (position == actPrevious.position) {
+ ; // Delete -> OK
+ } else {
+ // Removals must be at same position to coalesce
+ currentAction++;
+ }
+ } else {
+ // Removals must be of one character to coalesce
+ currentAction++;
+ }
+ } else {
+ //Platform::DebugPrintf("action coalesced\n");
+ }
+
+ } else {
+ // Actions not at top level are always coalesced unless this is after return to top level
+ if (!actions[currentAction].mayCoalesce)
+ currentAction++;
+ }
+ } else {
+ currentAction++;
+ }
+ actions[currentAction].Create(at, position, data, lengthData);
+ currentAction++;
+ actions[currentAction].Create(startAction);
+ maxAction = currentAction;
+}
+
+void UndoHistory::BeginUndoAction() {
+ EnsureUndoRoom();
+ if (undoSequenceDepth == 0) {
+ if (actions[currentAction].at != startAction) {
+ currentAction++;
+ actions[currentAction].Create(startAction);
+ maxAction = currentAction;
+ }
+ actions[currentAction].mayCoalesce = false;
+ }
+ undoSequenceDepth++;
+}
+
+void UndoHistory::EndUndoAction() {
+ EnsureUndoRoom();
+ undoSequenceDepth--;
+ if (0 == undoSequenceDepth) {
+ if (actions[currentAction].at != startAction) {
+ currentAction++;
+ actions[currentAction].Create(startAction);
+ maxAction = currentAction;
+ }
+ actions[currentAction].mayCoalesce = false;
+ }
+}
+
+void UndoHistory::DropUndoSequence() {
+ undoSequenceDepth = 0;
+}
+
+void UndoHistory::DeleteUndoHistory() {
+ for (int i = 1; i < maxAction; i++)
+ actions[i].Destroy();
+ maxAction = 0;
+ currentAction = 0;
+ actions[currentAction].Create(startAction);
+ savePoint = 0;
+}
+
+void UndoHistory::SetSavePoint() {
+ savePoint = currentAction;
+}
+
+bool UndoHistory::IsSavePoint() const {
+ return savePoint == currentAction;
+}
+
+bool UndoHistory::CanUndo() const {
+ return (currentAction > 0) && (maxAction > 0);
+}
+
+int UndoHistory::StartUndo() {
+ // Drop any trailing startAction
+ if (actions[currentAction].at == startAction && currentAction > 0)
+ currentAction--;
+
+ // Count the steps in this action
+ int act = currentAction;
+ while (actions[act].at != startAction && act > 0) {
+ act--;
+ }
+ return currentAction - act;
+}
+
+const Action &UndoHistory::GetUndoStep() const {
+ return actions[currentAction];
+}
+
+void UndoHistory::CompletedUndoStep() {
+ currentAction--;
+}
+
+bool UndoHistory::CanRedo() const {
+ return maxAction > currentAction;
+}
+
+int UndoHistory::StartRedo() {
+ // Drop any leading startAction
+ if (actions[currentAction].at == startAction && currentAction < maxAction)
+ currentAction++;
+
+ // Count the steps in this action
+ int act = currentAction;
+ while (actions[act].at != startAction && act < maxAction) {
+ act++;
+ }
+ return act - currentAction;
+}
+
+const Action &UndoHistory::GetRedoStep() const {
+ return actions[currentAction];
+}
+
+void UndoHistory::CompletedRedoStep() {
+ currentAction++;
+}
+
+CellBuffer::CellBuffer(int initialLength) {
+ body = new char[initialLength];
+ size = initialLength;
+ length = 0;
+ part1len = 0;
+ gaplen = initialLength;
+ part2body = body + gaplen;
+ readOnly = false;
+ collectingUndo = true;
+ growSize = 4000;
+}
+
+CellBuffer::~CellBuffer() {
+ delete []body;
+ body = 0;
+}
+
+void CellBuffer::GapTo(int position) {
+ if (position == part1len)
+ return ;
+ if (position < part1len) {
+ int diff = part1len - position;
+ //Platform::DebugPrintf("Move gap backwards to %d diff = %d part1len=%d length=%d \n", position,diff, part1len, length);
+ for (int i = 0; i < diff; i++)
+ body[part1len + gaplen - i - 1] = body[part1len - i - 1];
+ } else { // position > part1len
+ int diff = position - part1len;
+ //Platform::DebugPrintf("Move gap forwards to %d diff =%d\n", position,diff);
+ for (int i = 0; i < diff; i++)
+ body[part1len + i] = body[part1len + gaplen + i];
+ }
+ part1len = position;
+ part2body = body + gaplen;
+}
+
+void CellBuffer::RoomFor(int insertionLength) {
+ //Platform::DebugPrintf("need room %d %d\n", gaplen, insertionLength);
+ if (gaplen <= insertionLength) {
+ //Platform::DebugPrintf("need room %d %d\n", gaplen, insertionLength);
+ if (growSize * 6 < size)
+ growSize *= 2;
+ int newSize = size + insertionLength + growSize;
+ Allocate(newSize);
+ }
+}
+
+// To make it easier to write code that uses ByteAt, a position outside the range of the buffer
+// can be retrieved. All characters outside the range have the value '\0'.
+char CellBuffer::ByteAt(int position) {
+ if (position < part1len) {
+ if (position < 0) {
+ return '\0';
+ } else {
+ return body[position];
+ }
+ } else {
+ if (position >= length) {
+ return '\0';
+ } else {
+ return part2body[position];
+ }
+ }
+}
+
+void CellBuffer::SetByteAt(int position, char ch) {
+
+ if (position < 0) {
+ //Platform::DebugPrintf("Bad position %d\n",position);
+ return ;
+ }
+ if (position >= length + 11) {
+ Platform::DebugPrintf("Very Bad position %d of %d\n", position, length);
+ //exit(2);
+ return ;
+ }
+ if (position >= length) {
+ //Platform::DebugPrintf("Bad position %d of %d\n",position,length);
+ return ;
+ }
+
+ if (position < part1len) {
+ body[position] = ch;
+ } else {
+ part2body[position] = ch;
+ }
+}
+
+char CellBuffer::CharAt(int position) {
+ return ByteAt(position*2);
+}
+
+void CellBuffer::GetCharRange(char *buffer, int position, int lengthRetrieve) {
+ if (lengthRetrieve < 0)
+ return ;
+ if (position < 0)
+ return ;
+ int bytePos = position * 2;
+ if ((bytePos + lengthRetrieve * 2) > length) {
+ Platform::DebugPrintf("Bad GetCharRange %d for %d of %d\n", bytePos,
+ lengthRetrieve, length);
+ return ;
+ }
+ GapTo(0); // Move the buffer so its easy to subscript into it
+ char *pb = part2body + bytePos;
+ while (lengthRetrieve--) {
+ *buffer++ = *pb;
+ pb += 2;
+ }
+}
+
+char CellBuffer::StyleAt(int position) {
+ return ByteAt(position*2 + 1);
+}
+
+const char *CellBuffer::InsertString(int position, char *s, int insertLength) {
+ char *data = 0;
+ // InsertString and DeleteChars are the bottleneck though which all changes occur
+ if (!readOnly) {
+ if (collectingUndo) {
+ // Save into the undo/redo stack, but only the characters - not the formatting
+ // This takes up about half load time
+ data = new char[insertLength / 2];
+ for (int i = 0; i < insertLength / 2; i++) {
+ data[i] = s[i * 2];
+ }
+ uh.AppendAction(insertAction, position / 2, data, insertLength / 2);
+ }
+
+ BasicInsertString(position, s, insertLength);
+ }
+ return data;
+}
+
+bool CellBuffer::SetStyleAt(int position, char style, char mask) {
+ style &= mask;
+ char curVal = ByteAt(position * 2 + 1);
+ if ((curVal & mask) != style) {
+ SetByteAt(position*2 + 1, static_cast<char>((curVal & ~mask) | style));
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool CellBuffer::SetStyleFor(int position, int lengthStyle, char style, char mask) {
+ int bytePos = position * 2 + 1;
+ bool changed = false;
+ PLATFORM_ASSERT(lengthStyle == 0 ||
+ (lengthStyle > 0 && lengthStyle + position < length));
+ while (lengthStyle--) {
+ char curVal = ByteAt(bytePos);
+ if ((curVal & mask) != style) {
+ SetByteAt(bytePos, static_cast<char>((curVal & ~mask) | style));
+ changed = true;
+ }
+ bytePos += 2;
+ }
+ return changed;
+}
+
+const char *CellBuffer::DeleteChars(int position, int deleteLength) {
+ // InsertString and DeleteChars are the bottleneck though which all changes occur
+ PLATFORM_ASSERT(deleteLength > 0);
+ char *data = 0;
+ if (!readOnly) {
+ if (collectingUndo) {
+ // Save into the undo/redo stack, but only the characters - not the formatting
+ data = new char[deleteLength / 2];
+ for (int i = 0; i < deleteLength / 2; i++) {
+ data[i] = ByteAt(position + i * 2);
+ }
+ uh.AppendAction(removeAction, position / 2, data, deleteLength / 2);
+ }
+
+ BasicDeleteChars(position, deleteLength);
+ }
+ return data;
+}
+
+int CellBuffer::ByteLength() {
+ return length;
+}
+
+int CellBuffer::Length() {
+ return ByteLength() / 2;
+}
+
+void CellBuffer::Allocate(int newSize) {
+ if (newSize > length) {
+ GapTo(length);
+ char *newBody = new char[newSize];
+ memcpy(newBody, body, length);
+ delete []body;
+ body = newBody;
+ gaplen += newSize - size;
+ part2body = body + gaplen;
+ size = newSize;
+ }
+}
+
+int CellBuffer::Lines() {
+ //Platform::DebugPrintf("Lines = %d\n", lv.lines);
+ return lv.lines;
+}
+
+int CellBuffer::LineStart(int line) {
+ if (line < 0)
+ return 0;
+ else if (line > lv.lines)
+ return Length();
+ else
+ return lv.linesData[line].startPosition;
+}
+
+bool CellBuffer::IsReadOnly() {
+ return readOnly;
+}
+
+void CellBuffer::SetReadOnly(bool set) {
+ readOnly = set;
+}
+
+void CellBuffer::SetSavePoint() {
+ uh.SetSavePoint();
+}
+
+bool CellBuffer::IsSavePoint() {
+ return uh.IsSavePoint();
+}
+
+int CellBuffer::AddMark(int line, int markerNum) {
+ if ((line >= 0) && (line < lv.lines)) {
+ return lv.AddMark(line, markerNum);
+ }
+ return - 1;
+}
+
+void CellBuffer::DeleteMark(int line, int markerNum) {
+ if ((line >= 0) && (line < lv.lines)) {
+ lv.DeleteMark(line, markerNum, false);
+ }
+}
+
+void CellBuffer::DeleteMarkFromHandle(int markerHandle) {
+ lv.DeleteMarkFromHandle(markerHandle);
+}
+
+int CellBuffer::GetMark(int line) {
+ if ((line >= 0) && (line < lv.lines) && (lv.linesData[line].handleSet))
+ return lv.linesData[line].handleSet->MarkValue();
+ return 0;
+}
+
+void CellBuffer::DeleteAllMarks(int markerNum) {
+ for (int line = 0; line < lv.lines; line++) {
+ lv.DeleteMark(line, markerNum, true);
+ }
+}
+
+int CellBuffer::LineFromHandle(int markerHandle) {
+ return lv.LineFromHandle(markerHandle);
+}
+
+// Without undo
+
+void CellBuffer::BasicInsertString(int position, char *s, int insertLength) {
+ //Platform::DebugPrintf("Inserting at %d for %d\n", position, insertLength);
+ if (insertLength == 0)
+ return ;
+ PLATFORM_ASSERT(insertLength > 0);
+ RoomFor(insertLength);
+ GapTo(position);
+
+ memcpy(body + part1len, s, insertLength);
+ length += insertLength;
+ part1len += insertLength;
+ gaplen -= insertLength;
+ part2body = body + gaplen;
+
+ int lineInsert = lv.LineFromPosition(position / 2) + 1;
+ // Point all the lines after the insertion point further along in the buffer
+ for (int lineAfter = lineInsert; lineAfter <= lv.lines; lineAfter++) {
+ lv.linesData[lineAfter].startPosition += insertLength / 2;
+ }
+ char chPrev = ' ';
+ if ((position - 2) >= 0)
+ chPrev = ByteAt(position - 2);
+ char chAfter = ' ';
+ if ((position + insertLength) < length)
+ chAfter = ByteAt(position + insertLength);
+ if (chPrev == '\r' && chAfter == '\n') {
+ //Platform::DebugPrintf("Splitting a crlf pair at %d\n", lineInsert);
+ // Splitting up a crlf pair at position
+ lv.InsertValue(lineInsert, position / 2);
+ lineInsert++;
+ }
+ char ch = ' ';
+ for (int i = 0; i < insertLength; i += 2) {
+ ch = s[i];
+ if (ch == '\r') {
+ //Platform::DebugPrintf("Inserting cr at %d\n", lineInsert);
+ lv.InsertValue(lineInsert, (position + i) / 2 + 1);
+ lineInsert++;
+ } else if (ch == '\n') {
+ if (chPrev == '\r') {
+ //Platform::DebugPrintf("Patching cr before lf at %d\n", lineInsert-1);
+ // Patch up what was end of line
+ lv.SetValue(lineInsert - 1, (position + i) / 2 + 1);
+ } else {
+ //Platform::DebugPrintf("Inserting lf at %d\n", lineInsert);
+ lv.InsertValue(lineInsert, (position + i) / 2 + 1);
+ lineInsert++;
+ }
+ }
+ chPrev = ch;
+ }
+ // Joining two lines where last insertion is cr and following text starts with lf
+ if (chAfter == '\n') {
+ if (ch == '\r') {
+ //Platform::DebugPrintf("Joining cr before lf at %d\n", lineInsert-1);
+ // End of line already in buffer so drop the newly created one
+ lv.Remove(lineInsert - 1);
+ }
+ }
+}
+
+void CellBuffer::BasicDeleteChars(int position, int deleteLength) {
+ //Platform::DebugPrintf("Deleting at %d for %d\n", position, deleteLength);
+ if (deleteLength == 0)
+ return ;
+
+ if ((position == 0) && (deleteLength == length)) {
+ // If whole buffer is being deleted, faster to reinitialise lines data
+ // than to delete each line.
+ //printf("Whole buffer being deleted\n");
+ lv.Init();
+ } else {
+ // Have to fix up line positions before doing deletion as looking at text in buffer
+ // to work out which lines have been removed
+
+ int lineRemove = lv.LineFromPosition(position / 2) + 1;
+ // Point all the lines after the insertion point further along in the buffer
+ for (int lineAfter = lineRemove; lineAfter <= lv.lines; lineAfter++) {
+ lv.linesData[lineAfter].startPosition -= deleteLength / 2;
+ }
+ char chPrev = ' ';
+ if (position >= 2)
+ chPrev = ByteAt(position - 2);
+ char chBefore = chPrev;
+ char chNext = ' ';
+ if (position < length)
+ chNext = ByteAt(position);
+ bool ignoreNL = false;
+ if (chPrev == '\r' && chNext == '\n') {
+ //Platform::DebugPrintf("Deleting lf after cr, move line end to cr at %d\n", lineRemove);
+ // Move back one
+ lv.SetValue(lineRemove, position / 2);
+ lineRemove++;
+ ignoreNL = true; // First \n is not real deletion
+ }
+
+ char ch = chNext;
+ for (int i = 0; i < deleteLength; i += 2) {
+ chNext = ' ';
+ if ((position + i + 2) < length)
+ chNext = ByteAt(position + i + 2);
+ //Platform::DebugPrintf("Deleting %d %x\n", i, ch);
+ if (ch == '\r') {
+ if (chNext != '\n') {
+ //Platform::DebugPrintf("Removing cr end of line\n");
+ lv.Remove(lineRemove);
+ }
+ } else if (ch == '\n') {
+ if (ignoreNL) {
+ ignoreNL = false; // Further \n are real deletions
+ } else {
+ //Platform::DebugPrintf("Removing lf end of line\n");
+ lv.Remove(lineRemove);
+ }
+ }
+
+ ch = chNext;
+ }
+ // May have to fix up end if last deletion causes cr to be next to lf
+ // or removes one of a crlf pair
+ char chAfter = ' ';
+ if ((position + deleteLength) < length)
+ chAfter = ByteAt(position + deleteLength);
+ if (chBefore == '\r' && chAfter == '\n') {
+ //d.printf("Joining cr before lf at %d\n", lineRemove);
+ // Using lineRemove-1 as cr ended line before start of deletion
+ lv.Remove(lineRemove - 1);
+ lv.SetValue(lineRemove - 1, position / 2 + 1);
+ }
+ }
+ GapTo(position);
+ length -= deleteLength;
+ gaplen += deleteLength;
+ part2body = body + gaplen;
+}
+
+bool CellBuffer::SetUndoCollection(bool collectUndo) {
+ collectingUndo = collectUndo;
+ uh.DropUndoSequence();
+ return collectingUndo;
+}
+
+bool CellBuffer::IsCollectingUndo() {
+ return collectingUndo;
+}
+
+void CellBuffer::BeginUndoAction() {
+ uh.BeginUndoAction();
+}
+
+void CellBuffer::EndUndoAction() {
+ uh.EndUndoAction();
+}
+
+void CellBuffer::DeleteUndoHistory() {
+ uh.DeleteUndoHistory();
+}
+
+bool CellBuffer::CanUndo() {
+ return uh.CanUndo();
+}
+
+int CellBuffer::StartUndo() {
+ return uh.StartUndo();
+}
+
+const Action &CellBuffer::GetUndoStep() const {
+ return uh.GetUndoStep();
+}
+
+void CellBuffer::PerformUndoStep() {
+ const Action &actionStep = uh.GetUndoStep();
+ if (actionStep.at == insertAction) {
+ BasicDeleteChars(actionStep.position*2, actionStep.lenData*2);
+ } else if (actionStep.at == removeAction) {
+ char *styledData = new char[actionStep.lenData * 2];
+ for (int i = 0; i < actionStep.lenData; i++) {
+ styledData[i*2] = actionStep.data[i];
+ styledData[i*2 + 1] = 0;
+ }
+ BasicInsertString(actionStep.position*2, styledData, actionStep.lenData*2);
+ delete []styledData;
+ }
+ uh.CompletedUndoStep();
+}
+
+bool CellBuffer::CanRedo() {
+ return uh.CanRedo();
+}
+
+int CellBuffer::StartRedo() {
+ return uh.StartRedo();
+}
+
+const Action &CellBuffer::GetRedoStep() const {
+ return uh.GetRedoStep();
+}
+
+void CellBuffer::PerformRedoStep() {
+ const Action &actionStep = uh.GetRedoStep();
+ if (actionStep.at == insertAction) {
+ char *styledData = new char[actionStep.lenData * 2];
+ for (int i = 0; i < actionStep.lenData; i++) {
+ styledData[i*2] = actionStep.data[i];
+ styledData[i*2 + 1] = 0;
+ }
+ BasicInsertString(actionStep.position*2, styledData, actionStep.lenData*2);
+ delete []styledData;
+ } else if (actionStep.at == removeAction) {
+ BasicDeleteChars(actionStep.position*2, actionStep.lenData*2);
+ }
+ uh.CompletedRedoStep();
+}
+
+int CellBuffer::SetLineState(int line, int state) {
+ int stateOld = lineStates[line];
+ lineStates[line] = state;
+ return stateOld;
+}
+
+int CellBuffer::GetLineState(int line) {
+ return lineStates[line];
+}
+
+int CellBuffer::GetMaxLineState() {
+ return lineStates.Length();
+}
+
+int CellBuffer::SetLevel(int line, int level) {
+ int prev = 0;
+ if ((line >= 0) && (line < lv.lines)) {
+ if (!lv.levels) {
+ lv.ExpandLevels();
+ }
+ prev = lv.levels[line];
+ if (lv.levels[line] != level) {
+ lv.levels[line] = level;
+ }
+ }
+ return prev;
+}
+
+int CellBuffer::GetLevel(int line) {
+ if (lv.levels && (line >= 0) && (line < lv.lines)) {
+ return lv.levels[line];
+ } else {
+ return SC_FOLDLEVELBASE;
+ }
+}
+
+void CellBuffer::ClearLevels() {
+ lv.ClearLevels();
+}
diff --git a/src/CellBuffer.h b/src/CellBuffer.h
new file mode 100755
index 0000000..bb81fd5
--- /dev/null
+++ b/src/CellBuffer.h
@@ -0,0 +1,250 @@
+// Scintilla source code edit control
+/** @file CellBuffer.h
+ ** Manages the text of the document.
+ **/
+// Copyright 1998-2004 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef CELLBUFFER_H
+#define CELLBUFFER_H
+
+/**
+ * This holds the marker identifier and the marker type to display.
+ * MarkerHandleNumbers are members of lists.
+ */
+struct MarkerHandleNumber {
+ int handle;
+ int number;
+ MarkerHandleNumber *next;
+};
+
+/**
+ * A marker handle set contains any number of MarkerHandleNumbers.
+ */
+class MarkerHandleSet {
+ MarkerHandleNumber *root;
+
+public:
+ MarkerHandleSet();
+ ~MarkerHandleSet();
+ int Length();
+ int NumberFromHandle(int handle);
+ int MarkValue(); ///< Bit set of marker numbers.
+ bool Contains(int handle);
+ bool InsertHandle(int handle, int markerNum);
+ void RemoveHandle(int handle);
+ bool RemoveNumber(int markerNum);
+ void CombineWith(MarkerHandleSet *other);
+};
+
+/**
+ * Each line stores the starting position of the first character of the line in the cell buffer
+ * and potentially a marker handle set. Often a line will not have any attached markers.
+ */
+struct LineData {
+ int startPosition;
+ MarkerHandleSet *handleSet;
+ LineData() : startPosition(0), handleSet(0) {
+ }
+};
+
+/**
+ * The line vector contains information about each of the lines in a cell buffer.
+ */
+class LineVector {
+public:
+ int growSize;
+ int lines;
+ LineData *linesData;
+ int size;
+ int *levels;
+ int sizeLevels;
+
+ /// Handles are allocated sequentially and should never have to be reused as 32 bit ints are very big.
+ int handleCurrent;
+
+ LineVector();
+ ~LineVector();
+ void Init();
+
+ void Expand(int sizeNew);
+ void ExpandLevels(int sizeNew=-1);
+ void ClearLevels();
+ void InsertValue(int pos, int value);
+ void SetValue(int pos, int value);
+ void Remove(int pos);
+ int LineFromPosition(int pos);
+
+ int AddMark(int line, int marker);
+ void MergeMarkers(int pos);
+ void DeleteMark(int line, int markerNum, bool all);
+ void DeleteMarkFromHandle(int markerHandle);
+ int LineFromHandle(int markerHandle);
+};
+
+enum actionType { insertAction, removeAction, startAction };
+
+/**
+ * Actions are used to store all the information required to perform one undo/redo step.
+ */
+class Action {
+public:
+ actionType at;
+ int position;
+ char *data;
+ int lenData;
+ bool mayCoalesce;
+
+ Action();
+ ~Action();
+ void Create(actionType at_, int position_=0, char *data_=0, int lenData_=0, bool mayCoalesce_=true);
+ void Destroy();
+ void Grab(Action *source);
+};
+
+/**
+ *
+ */
+class UndoHistory {
+ Action *actions;
+ int lenActions;
+ int maxAction;
+ int currentAction;
+ int undoSequenceDepth;
+ int savePoint;
+
+ void EnsureUndoRoom();
+
+public:
+ UndoHistory();
+ ~UndoHistory();
+
+ void AppendAction(actionType at, int position, char *data, int length);
+
+ void BeginUndoAction();
+ void EndUndoAction();
+ void DropUndoSequence();
+ void DeleteUndoHistory();
+
+ /// The save point is a marker in the undo stack where the container has stated that
+ /// the buffer was saved. Undo and redo can move over the save point.
+ void SetSavePoint();
+ bool IsSavePoint() const;
+
+ /// To perform an undo, StartUndo is called to retrieve the number of steps, then UndoStep is
+ /// called that many times. Similarly for redo.
+ bool CanUndo() const;
+ int StartUndo();
+ const Action &GetUndoStep() const;
+ void CompletedUndoStep();
+ bool CanRedo() const;
+ int StartRedo();
+ const Action &GetRedoStep() const;
+ void CompletedRedoStep();
+};
+
+/**
+ * Holder for an expandable array of characters that supports undo and line markers.
+ * Based on article "Data Structures in a Bit-Mapped Text Editor"
+ * by Wilfred J. Hansen, Byte January 1987, page 183.
+ */
+class CellBuffer {
+private:
+ char *body; ///< The cell buffer itself.
+ int size; ///< Allocated size of the buffer.
+ int length; ///< Total length of the data.
+ int part1len; ///< Length of the first part.
+ int gaplen; ///< Length of the gap between the two parts.
+ char *part2body; ///< The second part of the cell buffer.
+ ///< Doesn't point after the gap but set so that
+ ///< part2body[position] is consistent with body[position].
+ bool readOnly;
+ int growSize;
+
+ bool collectingUndo;
+ UndoHistory uh;
+
+ LineVector lv;
+
+ SVector lineStates;
+
+ void GapTo(int position);
+ void RoomFor(int insertionLength);
+
+ inline char ByteAt(int position);
+ void SetByteAt(int position, char ch);
+
+public:
+
+ CellBuffer(int initialLength = 4000);
+ ~CellBuffer();
+
+ /// Retrieving positions outside the range of the buffer works and returns 0
+ char CharAt(int position);
+ void GetCharRange(char *buffer, int position, int lengthRetrieve);
+ char StyleAt(int position);
+
+ int ByteLength();
+ int Length();
+ void Allocate(int newSize);
+ int Lines();
+ int LineStart(int line);
+ int LineFromPosition(int pos) { return lv.LineFromPosition(pos); }
+ const char *InsertString(int position, char *s, int insertLength);
+
+ /// Setting styles for positions outside the range of the buffer is safe and has no effect.
+ /// @return true if the style of a character is changed.
+ bool SetStyleAt(int position, char style, char mask='\377');
+ bool SetStyleFor(int position, int length, char style, char mask);
+
+ const char *DeleteChars(int position, int deleteLength);
+
+ bool IsReadOnly();
+ void SetReadOnly(bool set);
+
+ /// The save point is a marker in the undo stack where the container has stated that
+ /// the buffer was saved. Undo and redo can move over the save point.
+ void SetSavePoint();
+ bool IsSavePoint();
+
+ /// Line marker functions
+ int AddMark(int line, int markerNum);
+ void DeleteMark(int line, int markerNum);
+ void DeleteMarkFromHandle(int markerHandle);
+ int GetMark(int line);
+ void DeleteAllMarks(int markerNum);
+ int LineFromHandle(int markerHandle);
+
+ /// Actions without undo
+ void BasicInsertString(int position, char *s, int insertLength);
+ void BasicDeleteChars(int position, int deleteLength);
+
+ bool SetUndoCollection(bool collectUndo);
+ bool IsCollectingUndo();
+ void BeginUndoAction();
+ void EndUndoAction();
+ void DeleteUndoHistory();
+
+ /// To perform an undo, StartUndo is called to retrieve the number of steps, then UndoStep is
+ /// called that many times. Similarly for redo.
+ bool CanUndo();
+ int StartUndo();
+ const Action &GetUndoStep() const;
+ void PerformUndoStep();
+ bool CanRedo();
+ int StartRedo();
+ const Action &GetRedoStep() const;
+ void PerformRedoStep();
+
+ int SetLineState(int line, int state);
+ int GetLineState(int line);
+ int GetMaxLineState();
+
+ int SetLevel(int line, int level);
+ int GetLevel(int line);
+ void ClearLevels();
+};
+
+#define CELL_SIZE 2
+
+#endif
diff --git a/src/CharClassify.cpp b/src/CharClassify.cpp
new file mode 100644
index 0000000..acab4b2
--- /dev/null
+++ b/src/CharClassify.cpp
@@ -0,0 +1,43 @@
+// Scintilla source code edit control
+/** @file CharClassify.cxx
+ ** Character classifications used by Document and RESearch.
+ **/
+// Copyright 2006 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <ctype.h>
+
+#include "CharClassify.h"
+
+// Shut up annoying Visual C++ warnings:
+#ifdef _MSC_VER
+#pragma warning(disable: 4514)
+#endif
+
+CharClassify::CharClassify() {
+ SetDefaultCharClasses(true);
+}
+
+void CharClassify::SetDefaultCharClasses(bool includeWordClass) {
+ // Initialize all char classes to default values
+ for (int ch = 0; ch < 256; ch++) {
+ if (ch == '\r' || ch == '\n')
+ charClass[ch] = ccNewLine;
+ else if (ch < 0x20 || ch == ' ')
+ charClass[ch] = ccSpace;
+ else if (includeWordClass && (ch >= 0x80 || isalnum(ch) || ch == '_'))
+ charClass[ch] = ccWord;
+ else
+ charClass[ch] = ccPunctuation;
+ }
+}
+
+void CharClassify::SetCharClasses(const unsigned char *chars, cc newCharClass) {
+ // Apply the newCharClass to the specifed chars
+ if (chars) {
+ while (*chars) {
+ charClass[*chars] = static_cast<unsigned char>(newCharClass);
+ chars++;
+ }
+ }
+}
diff --git a/src/CharClassify.h b/src/CharClassify.h
new file mode 100644
index 0000000..881d3a1
--- /dev/null
+++ b/src/CharClassify.h
@@ -0,0 +1,25 @@
+// Scintilla source code edit control
+/** @file CharClassify.h
+ ** Character classifications used by Document and RESearch.
+ **/
+// Copyright 2006 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef CHARCLASSIFY_H
+#define CHARCLASSIFY_H
+
+class CharClassify {
+public:
+ CharClassify();
+
+ enum cc { ccSpace, ccNewLine, ccWord, ccPunctuation };
+ void SetDefaultCharClasses(bool includeWordClass);
+ void SetCharClasses(const unsigned char *chars, cc newCharClass);
+ cc GetClass(unsigned char ch) const { return static_cast<cc>(charClass[ch]);}
+ bool IsWord(unsigned char ch) const { return static_cast<cc>(charClass[ch]) == ccWord;}
+
+private:
+ enum { maxChar=256 };
+ unsigned char charClass[maxChar]; // not type cc to save space
+};
+#endif
diff --git a/src/ContractionState.cpp b/src/ContractionState.cpp
new file mode 100755
index 0000000..3d021b0
--- /dev/null
+++ b/src/ContractionState.cpp
@@ -0,0 +1,289 @@
+// Scintilla source code edit control
+/** @file ContractionState.cxx
+ ** Manages visibility of lines for folding.
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include "Platform.h"
+
+#include "ContractionState.h"
+
+OneLine::OneLine() {
+ displayLine = 0;
+ //docLine = 0;
+ visible = true;
+ height = 1;
+ expanded = true;
+}
+
+ContractionState::ContractionState() {
+ lines = 0;
+ size = 0;
+ linesInDoc = 1;
+ linesInDisplay = 1;
+ valid = false;
+ docLines = 0;
+ sizeDocLines = 0;
+}
+
+ContractionState::~ContractionState() {
+ Clear();
+}
+
+void ContractionState::MakeValid() const {
+ if (!valid) {
+ // Could be cleverer by keeping the index of the last still valid entry
+ // rather than invalidating all.
+ linesInDisplay = 0;
+ for (int lineInDoc=0; lineInDoc<linesInDoc; lineInDoc++) {
+ lines[lineInDoc].displayLine = linesInDisplay;
+ if (lines[lineInDoc].visible) {
+ linesInDisplay += lines[lineInDoc].height;
+ }
+ }
+ if (sizeDocLines < linesInDisplay) {
+ delete []docLines;
+ int *docLinesNew = new int[linesInDisplay + growSize];
+ if (!docLinesNew) {
+ docLines = 0;
+ sizeDocLines = 0;
+ return;
+ }
+ docLines = docLinesNew;
+ sizeDocLines = linesInDisplay + growSize;
+ }
+
+ int lineInDisplay=0;
+ for (int line=0; line<linesInDoc; line++) {
+ if (lines[line].visible) {
+ for (int linePiece=0; linePiece<lines[line].height; linePiece++) {
+ docLines[lineInDisplay] = line;
+ lineInDisplay++;
+ }
+ }
+ }
+ valid = true;
+ }
+}
+
+void ContractionState::Clear() {
+ delete []lines;
+ lines = 0;
+ size = 0;
+ linesInDoc = 1;
+ linesInDisplay = 1;
+ delete []docLines;
+ docLines = 0;
+ sizeDocLines = 0;
+}
+
+int ContractionState::LinesInDoc() const {
+ return linesInDoc;
+}
+
+int ContractionState::LinesDisplayed() const {
+ if (size != 0) {
+ MakeValid();
+ }
+ return linesInDisplay;
+}
+
+int ContractionState::DisplayFromDoc(int lineDoc) const {
+ if (size == 0) {
+ return lineDoc;
+ }
+ MakeValid();
+ if ((lineDoc >= 0) && (lineDoc < linesInDoc)) {
+ return lines[lineDoc].displayLine;
+ }
+ return -1;
+}
+
+int ContractionState::DocFromDisplay(int lineDisplay) const {
+ if (lineDisplay <= 0)
+ return 0;
+ if (lineDisplay >= linesInDisplay)
+ return linesInDoc;
+ if (size == 0)
+ return lineDisplay;
+ MakeValid();
+ if (docLines) { // Valid allocation
+ return docLines[lineDisplay];
+ } else {
+ return 0;
+ }
+}
+
+void ContractionState::Grow(int sizeNew) {
+ OneLine *linesNew = new OneLine[sizeNew];
+ if (linesNew) {
+ int i = 0;
+ for (; i < size; i++) {
+ linesNew[i] = lines[i];
+ }
+ for (; i < sizeNew; i++) {
+ linesNew[i].displayLine = i;
+ }
+ delete []lines;
+ lines = linesNew;
+ size = sizeNew;
+ valid = false;
+ } else {
+ Platform::DebugPrintf("No memory available\n");
+ // TODO: Blow up
+ }
+}
+
+void ContractionState::InsertLines(int lineDoc, int lineCount) {
+ if (size == 0) {
+ linesInDoc += lineCount;
+ linesInDisplay += lineCount;
+ return;
+ }
+ //Platform::DebugPrintf("InsertLine[%d] = %d\n", lineDoc);
+ if ((linesInDoc + lineCount + 2) >= size) {
+ Grow(linesInDoc + lineCount + growSize);
+ }
+ linesInDoc += lineCount;
+ for (int i = linesInDoc; i >= lineDoc + lineCount; i--) {
+ lines[i].visible = lines[i - lineCount].visible;
+ lines[i].height = lines[i - lineCount].height;
+ linesInDisplay += lines[i].height;
+ lines[i].expanded = lines[i - lineCount].expanded;
+ }
+ for (int d=0;d<lineCount;d++) {
+ lines[lineDoc+d].visible = true; // Should inherit visibility from context ?
+ lines[lineDoc+d].height = 1;
+ lines[lineDoc+d].expanded = true;
+ }
+ valid = false;
+}
+
+void ContractionState::DeleteLines(int lineDoc, int lineCount) {
+ if (size == 0) {
+ linesInDoc -= lineCount;
+ linesInDisplay -= lineCount;
+ return;
+ }
+ int deltaDisplayed = 0;
+ for (int d=0;d<lineCount;d++) {
+ if (lines[lineDoc+d].visible)
+ deltaDisplayed -= lines[lineDoc+d].height;
+ }
+ for (int i = lineDoc; i < linesInDoc-lineCount; i++) {
+ if (i != 0) // Line zero is always visible
+ lines[i].visible = lines[i + lineCount].visible;
+ lines[i].expanded = lines[i + lineCount].expanded;
+ lines[i].height = lines[i + lineCount].height;
+ }
+ linesInDoc -= lineCount;
+ linesInDisplay += deltaDisplayed;
+ valid = false;
+}
+
+bool ContractionState::GetVisible(int lineDoc) const {
+ if (size == 0)
+ return true;
+ if ((lineDoc >= 0) && (lineDoc < linesInDoc)) {
+ return lines[lineDoc].visible;
+ } else {
+ return false;
+ }
+}
+
+bool ContractionState::SetVisible(int lineDocStart, int lineDocEnd, bool visible) {
+ if (lineDocStart == 0)
+ lineDocStart++;
+ if (lineDocStart > lineDocEnd)
+ return false;
+ if (size == 0) {
+ Grow(linesInDoc + growSize);
+ }
+ // TODO: modify docLine members to mirror displayLine
+ int delta = 0;
+ // Change lineDocs
+ if ((lineDocStart <= lineDocEnd) && (lineDocStart >= 0) && (lineDocEnd < linesInDoc)) {
+ for (int line=lineDocStart; line <= lineDocEnd; line++) {
+ if (lines[line].visible != visible) {
+ delta += visible ? lines[line].height : -lines[line].height;
+ lines[line].visible = visible;
+ }
+ }
+ }
+ linesInDisplay += delta;
+ valid = false;
+ return delta != 0;
+}
+
+bool ContractionState::GetExpanded(int lineDoc) const {
+ if (size == 0)
+ return true;
+ if ((lineDoc >= 0) && (lineDoc < linesInDoc)) {
+ return lines[lineDoc].expanded;
+ } else {
+ return false;
+ }
+}
+
+bool ContractionState::SetExpanded(int lineDoc, bool expanded) {
+ if (size == 0) {
+ if (expanded) {
+ // If in completely expanded state then setting
+ // one line to expanded has no effect.
+ return false;
+ }
+ Grow(linesInDoc + growSize);
+ }
+ if ((lineDoc >= 0) && (lineDoc < linesInDoc)) {
+ if (lines[lineDoc].expanded != expanded) {
+ lines[lineDoc].expanded = expanded;
+ return true;
+ }
+ }
+ return false;
+}
+
+int ContractionState::GetHeight(int lineDoc) const {
+ if (size == 0)
+ return 1;
+ if ((lineDoc >= 0) && (lineDoc < linesInDoc)) {
+ return lines[lineDoc].height;
+ } else {
+ return 1;
+ }
+}
+
+// Set the number of display lines needed for this line.
+// Return true if this is a change.
+bool ContractionState::SetHeight(int lineDoc, int height) {
+ if (lineDoc > linesInDoc)
+ return false;
+ if (size == 0) {
+ if (height == 1) {
+ // If in completely expanded state then all lines
+ // assumed to have height of one so no effect here.
+ return false;
+ }
+ Grow(linesInDoc + growSize);
+ }
+ if (lines[lineDoc].height != height) {
+ lines[lineDoc].height = height;
+ valid = false;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void ContractionState::ShowAll() {
+ delete []lines;
+ lines = 0;
+ size = 0;
+
+ delete []docLines;
+ docLines = 0;
+ sizeDocLines = 0;
+
+ linesInDisplay = linesInDoc;
+}
diff --git a/src/ContractionState.h b/src/ContractionState.h
new file mode 100755
index 0000000..e15ee3b
--- /dev/null
+++ b/src/ContractionState.h
@@ -0,0 +1,65 @@
+// Scintilla source code edit control
+/** @file ContractionState.h
+ ** Manages visibility of lines for folding.
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef CONTRACTIONSTATE_H
+#define CONTRACTIONSTATE_H
+
+/**
+ */
+class OneLine {
+public:
+ int displayLine; ///< Position within set of visible lines
+ //int docLine; ///< Inverse of @a displayLine
+ int height; ///< Number of display lines needed to show all of the line
+ bool visible;
+ bool expanded;
+
+ OneLine();
+ virtual ~OneLine() {}
+};
+
+/**
+ */
+class ContractionState {
+ void Grow(int sizeNew);
+ enum { growSize = 4000 };
+ int linesInDoc;
+ mutable int linesInDisplay;
+ mutable OneLine *lines;
+ int size;
+ mutable int *docLines;
+ mutable int sizeDocLines;
+ mutable bool valid;
+ void MakeValid() const;
+
+public:
+ ContractionState();
+ virtual ~ContractionState();
+
+ void Clear();
+
+ int LinesInDoc() const;
+ int LinesDisplayed() const;
+ int DisplayFromDoc(int lineDoc) const;
+ int DocFromDisplay(int lineDisplay) const;
+
+ void InsertLines(int lineDoc, int lineCount);
+ void DeleteLines(int lineDoc, int lineCount);
+
+ bool GetVisible(int lineDoc) const;
+ bool SetVisible(int lineDocStart, int lineDocEnd, bool visible);
+
+ bool GetExpanded(int lineDoc) const;
+ bool SetExpanded(int lineDoc, bool expanded);
+
+ int GetHeight(int lineDoc) const;
+ bool SetHeight(int lineDoc, int height);
+
+ void ShowAll();
+};
+
+#endif
diff --git a/src/Document.cpp b/src/Document.cpp
new file mode 100755
index 0000000..92be926
--- /dev/null
+++ b/src/Document.cpp
@@ -0,0 +1,1577 @@
+// Scintilla source code edit control
+/** @file Document.cxx
+ ** Text document that handles notifications, DBCS, styling, words and end of line.
+ **/
+// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "Platform.h"
+
+#include "Scintilla.h"
+#include "SVector.h"
+#include "CellBuffer.h"
+#include "CharClassify.h"
+#include "Document.h"
+#include "RESearch.h"
+
+// This is ASCII specific but is safe with chars >= 0x80
+static inline bool isspacechar(unsigned char ch) {
+ return (ch == ' ') || ((ch >= 0x09) && (ch <= 0x0d));
+}
+
+static inline bool IsPunctuation(char ch) {
+ return isascii(ch) && ispunct(ch);
+}
+
+static inline bool IsADigit(char ch) {
+ return isascii(ch) && isdigit(ch);
+}
+
+static inline bool IsLowerCase(char ch) {
+ return isascii(ch) && islower(ch);
+}
+
+static inline bool IsUpperCase(char ch) {
+ return isascii(ch) && isupper(ch);
+}
+
+Document::Document() {
+ refCount = 0;
+#ifdef unix
+ eolMode = SC_EOL_LF;
+#else
+ eolMode = SC_EOL_CRLF;
+#endif
+ dbcsCodePage = 0;
+ stylingBits = 5;
+ stylingBitsMask = 0x1F;
+ stylingMask = 0;
+ endStyled = 0;
+ styleClock = 0;
+ enteredCount = 0;
+ enteredReadOnlyCount = 0;
+ tabInChars = 8;
+ indentInChars = 0;
+ actualIndentInChars = 8;
+ useTabs = true;
+ tabIndents = true;
+ backspaceUnindents = false;
+ watchers = 0;
+ lenWatchers = 0;
+
+ matchesValid = false;
+ pre = 0;
+ substituted = 0;
+}
+
+Document::~Document() {
+ for (int i = 0; i < lenWatchers; i++) {
+ watchers[i].watcher->NotifyDeleted(this, watchers[i].userData);
+ }
+ delete []watchers;
+ watchers = 0;
+ lenWatchers = 0;
+ delete pre;
+ pre = 0;
+ delete []substituted;
+ substituted = 0;
+}
+
+// Increase reference count and return its previous value.
+int Document::AddRef() {
+ return refCount++;
+}
+
+// Decrease reference count and return its previous value.
+// Delete the document if reference count reaches zero.
+int Document::Release() {
+ int curRefCount = --refCount;
+ if (curRefCount == 0)
+ delete this;
+ return curRefCount;
+}
+
+void Document::SetSavePoint() {
+ cb.SetSavePoint();
+ NotifySavePoint(true);
+}
+
+int Document::AddMark(int line, int markerNum) {
+ int prev = cb.AddMark(line, markerNum);
+ DocModification mh(SC_MOD_CHANGEMARKER, LineStart(line), 0, 0, 0, line);
+ mh.line = line;
+ NotifyModified(mh);
+ return prev;
+}
+
+void Document::AddMarkSet(int line, int valueSet) {
+ unsigned int m = valueSet;
+ for (int i = 0; m; i++, m >>= 1)
+ if (m & 1)
+ cb.AddMark(line, i);
+ DocModification mh(SC_MOD_CHANGEMARKER, LineStart(line), 0, 0, 0, line);
+ mh.line = line;
+ NotifyModified(mh);
+}
+
+void Document::DeleteMark(int line, int markerNum) {
+ cb.DeleteMark(line, markerNum);
+ DocModification mh(SC_MOD_CHANGEMARKER, LineStart(line), 0, 0, 0, line);
+ mh.line = line;
+ NotifyModified(mh);
+}
+
+void Document::DeleteMarkFromHandle(int markerHandle) {
+ cb.DeleteMarkFromHandle(markerHandle);
+ DocModification mh(SC_MOD_CHANGEMARKER, 0, 0, 0, 0);
+ mh.line = -1;
+ NotifyModified(mh);
+}
+
+void Document::DeleteAllMarks(int markerNum) {
+ cb.DeleteAllMarks(markerNum);
+ DocModification mh(SC_MOD_CHANGEMARKER, 0, 0, 0, 0);
+ mh.line = -1;
+ NotifyModified(mh);
+}
+
+int Document::LineStart(int line) {
+ return cb.LineStart(line);
+}
+
+int Document::LineEnd(int line) {
+ if (line == LinesTotal() - 1) {
+ return LineStart(line + 1);
+ } else {
+ int position = LineStart(line + 1) - 1;
+ // When line terminator is CR+LF, may need to go back one more
+ if ((position > LineStart(line)) && (cb.CharAt(position - 1) == '\r')) {
+ position--;
+ }
+ return position;
+ }
+}
+
+int Document::LineFromPosition(int pos) {
+ return cb.LineFromPosition(pos);
+}
+
+int Document::LineEndPosition(int position) {
+ return LineEnd(LineFromPosition(position));
+}
+
+int Document::VCHomePosition(int position) {
+ int line = LineFromPosition(position);
+ int startPosition = LineStart(line);
+ int endLine = LineStart(line + 1) - 1;
+ int startText = startPosition;
+ while (startText < endLine && (cb.CharAt(startText) == ' ' || cb.CharAt(startText) == '\t' ) )
+ startText++;
+ if (position == startText)
+ return startPosition;
+ else
+ return startText;
+}
+
+int Document::SetLevel(int line, int level) {
+ int prev = cb.SetLevel(line, level);
+ if (prev != level) {
+ DocModification mh(SC_MOD_CHANGEFOLD | SC_MOD_CHANGEMARKER,
+ LineStart(line), 0, 0, 0);
+ mh.line = line;
+ mh.foldLevelNow = level;
+ mh.foldLevelPrev = prev;
+ NotifyModified(mh);
+ }
+ return prev;
+}
+
+static bool IsSubordinate(int levelStart, int levelTry) {
+ if (levelTry & SC_FOLDLEVELWHITEFLAG)
+ return true;
+ else
+ return (levelStart & SC_FOLDLEVELNUMBERMASK) < (levelTry & SC_FOLDLEVELNUMBERMASK);
+}
+
+int Document::GetLastChild(int lineParent, int level) {
+ if (level == -1)
+ level = GetLevel(lineParent) & SC_FOLDLEVELNUMBERMASK;
+ int maxLine = LinesTotal();
+ int lineMaxSubord = lineParent;
+ while (lineMaxSubord < maxLine - 1) {
+ EnsureStyledTo(LineStart(lineMaxSubord + 2));
+ if (!IsSubordinate(level, GetLevel(lineMaxSubord + 1)))
+ break;
+ lineMaxSubord++;
+ }
+ if (lineMaxSubord > lineParent) {
+ if (level > (GetLevel(lineMaxSubord + 1) & SC_FOLDLEVELNUMBERMASK)) {
+ // Have chewed up some whitespace that belongs to a parent so seek back
+ if (GetLevel(lineMaxSubord) & SC_FOLDLEVELWHITEFLAG) {
+ lineMaxSubord--;
+ }
+ }
+ }
+ return lineMaxSubord;
+}
+
+int Document::GetFoldParent(int line) {
+ int level = GetLevel(line) & SC_FOLDLEVELNUMBERMASK;
+ int lineLook = line - 1;
+ while ((lineLook > 0) && (
+ (!(GetLevel(lineLook) & SC_FOLDLEVELHEADERFLAG)) ||
+ ((GetLevel(lineLook) & SC_FOLDLEVELNUMBERMASK) >= level))
+ ) {
+ lineLook--;
+ }
+ if ((GetLevel(lineLook) & SC_FOLDLEVELHEADERFLAG) &&
+ ((GetLevel(lineLook) & SC_FOLDLEVELNUMBERMASK) < level)) {
+ return lineLook;
+ } else {
+ return -1;
+ }
+}
+
+int Document::ClampPositionIntoDocument(int pos) {
+ return Platform::Clamp(pos, 0, Length());
+}
+
+bool Document::IsCrLf(int pos) {
+ if (pos < 0)
+ return false;
+ if (pos >= (Length() - 1))
+ return false;
+ return (cb.CharAt(pos) == '\r') && (cb.CharAt(pos + 1) == '\n');
+}
+
+static const int maxBytesInDBCSCharacter=5;
+
+int Document::LenChar(int pos) {
+ if (pos < 0) {
+ return 1;
+ } else if (IsCrLf(pos)) {
+ return 2;
+ } else if (SC_CP_UTF8 == dbcsCodePage) {
+ unsigned char ch = static_cast<unsigned char>(cb.CharAt(pos));
+ if (ch < 0x80)
+ return 1;
+ int len = 2;
+ if (ch >= (0x80 + 0x40 + 0x20))
+ len = 3;
+ int lengthDoc = Length();
+ if ((pos + len) > lengthDoc)
+ return lengthDoc -pos;
+ else
+ return len;
+ } else if (dbcsCodePage) {
+ char mbstr[maxBytesInDBCSCharacter+1];
+ int i;
+ for (i=0; i<Platform::DBCSCharMaxLength(); i++) {
+ mbstr[i] = cb.CharAt(pos+i);
+ }
+ mbstr[i] = '\0';
+ return Platform::DBCSCharLength(dbcsCodePage, mbstr);
+ } else {
+ return 1;
+ }
+}
+
+// Normalise a position so that it is not halfway through a two byte character.
+// This can occur in two situations -
+// When lines are terminated with \r\n pairs which should be treated as one character.
+// When displaying DBCS text such as Japanese.
+// If moving, move the position in the indicated direction.
+int Document::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) {
+ //Platform::DebugPrintf("NoCRLF %d %d\n", pos, moveDir);
+ // If out of range, just return minimum/maximum value.
+ if (pos <= 0)
+ return 0;
+ if (pos >= Length())
+ return Length();
+
+ // PLATFORM_ASSERT(pos > 0 && pos < Length());
+ if (checkLineEnd && IsCrLf(pos - 1)) {
+ if (moveDir > 0)
+ return pos + 1;
+ else
+ return pos - 1;
+ }
+
+ // Not between CR and LF
+
+ if (dbcsCodePage) {
+ if (SC_CP_UTF8 == dbcsCodePage) {
+ unsigned char ch = static_cast<unsigned char>(cb.CharAt(pos));
+ while ((pos > 0) && (pos < Length()) && (ch >= 0x80) && (ch < (0x80 + 0x40))) {
+ // ch is a trail byte
+ if (moveDir > 0)
+ pos++;
+ else
+ pos--;
+ ch = static_cast<unsigned char>(cb.CharAt(pos));
+ }
+ } else {
+ // Anchor DBCS calculations at start of line because start of line can
+ // not be a DBCS trail byte.
+ int posCheck = LineStart(LineFromPosition(pos));
+ while (posCheck < pos) {
+ char mbstr[maxBytesInDBCSCharacter+1];
+ int i;
+ for(i=0;i<Platform::DBCSCharMaxLength();i++) {
+ mbstr[i] = cb.CharAt(posCheck+i);
+ }
+ mbstr[i] = '\0';
+
+ int mbsize = Platform::DBCSCharLength(dbcsCodePage, mbstr);
+ if (posCheck + mbsize == pos) {
+ return pos;
+ } else if (posCheck + mbsize > pos) {
+ if (moveDir > 0) {
+ return posCheck + mbsize;
+ } else {
+ return posCheck;
+ }
+ }
+ posCheck += mbsize;
+ }
+ }
+ }
+
+ return pos;
+}
+
+void Document::ModifiedAt(int pos) {
+ if (endStyled > pos)
+ endStyled = pos;
+}
+
+void Document::CheckReadOnly() {
+ if (cb.IsReadOnly() && enteredReadOnlyCount == 0) {
+ enteredReadOnlyCount++;
+ NotifyModifyAttempt();
+ enteredReadOnlyCount--;
+ }
+}
+
+// Document only modified by gateways DeleteChars, InsertStyledString, Undo, Redo, and SetStyleAt.
+// SetStyleAt does not change the persistent state of a document
+
+// Unlike Undo, Redo, and InsertStyledString, the pos argument is a cell number not a char number
+bool Document::DeleteChars(int pos, int len) {
+ if (len == 0)
+ return false;
+ if ((pos + len) > Length())
+ return false;
+ CheckReadOnly();
+ if (enteredCount != 0) {
+ return false;
+ } else {
+ enteredCount++;
+ if (!cb.IsReadOnly()) {
+ NotifyModified(
+ DocModification(
+ SC_MOD_BEFOREDELETE | SC_PERFORMED_USER,
+ pos, len,
+ 0, 0));
+ int prevLinesTotal = LinesTotal();
+ bool startSavePoint = cb.IsSavePoint();
+ const char *text = cb.DeleteChars(pos * 2, len * 2);
+ if (startSavePoint && cb.IsCollectingUndo())
+ NotifySavePoint(!startSavePoint);
+ if ((pos < Length()) || (pos == 0))
+ ModifiedAt(pos);
+ else
+ ModifiedAt(pos-1);
+ NotifyModified(
+ DocModification(
+ SC_MOD_DELETETEXT | SC_PERFORMED_USER,
+ pos, len,
+ LinesTotal() - prevLinesTotal, text));
+ }
+ enteredCount--;
+ }
+ return !cb.IsReadOnly();
+}
+
+/**
+ * Insert a styled string (char/style pairs) with a length.
+ */
+bool Document::InsertStyledString(int position, char *s, int insertLength) {
+ CheckReadOnly();
+ if (enteredCount != 0) {
+ return false;
+ } else {
+ enteredCount++;
+ if (!cb.IsReadOnly()) {
+ NotifyModified(
+ DocModification(
+ SC_MOD_BEFOREINSERT | SC_PERFORMED_USER,
+ position / 2, insertLength / 2,
+ 0, s));
+ int prevLinesTotal = LinesTotal();
+ bool startSavePoint = cb.IsSavePoint();
+ const char *text = cb.InsertString(position, s, insertLength);
+ if (startSavePoint && cb.IsCollectingUndo())
+ NotifySavePoint(!startSavePoint);
+ ModifiedAt(position / 2);
+ NotifyModified(
+ DocModification(
+ SC_MOD_INSERTTEXT | SC_PERFORMED_USER,
+ position / 2, insertLength / 2,
+ LinesTotal() - prevLinesTotal, text));
+ }
+ enteredCount--;
+ }
+ return !cb.IsReadOnly();
+}
+
+int Document::Undo() {
+ int newPos = -1;
+ CheckReadOnly();
+ if (enteredCount == 0) {
+ enteredCount++;
+ if (!cb.IsReadOnly()) {
+ bool startSavePoint = cb.IsSavePoint();
+ bool multiLine = false;
+ int steps = cb.StartUndo();
+ //Platform::DebugPrintf("Steps=%d\n", steps);
+ for (int step = 0; step < steps; step++) {
+ const int prevLinesTotal = LinesTotal();
+ const Action &action = cb.GetUndoStep();
+ if (action.at == removeAction) {
+ NotifyModified(DocModification(
+ SC_MOD_BEFOREINSERT | SC_PERFORMED_UNDO, action));
+ } else {
+ NotifyModified(DocModification(
+ SC_MOD_BEFOREDELETE | SC_PERFORMED_UNDO, action));
+ }
+ cb.PerformUndoStep();
+ int cellPosition = action.position;
+ ModifiedAt(cellPosition);
+ newPos = cellPosition;
+
+ int modFlags = SC_PERFORMED_UNDO;
+ // With undo, an insertion action becomes a deletion notification
+ if (action.at == removeAction) {
+ newPos += action.lenData;
+ modFlags |= SC_MOD_INSERTTEXT;
+ } else {
+ modFlags |= SC_MOD_DELETETEXT;
+ }
+ if (steps > 1)
+ modFlags |= SC_MULTISTEPUNDOREDO;
+ const int linesAdded = LinesTotal() - prevLinesTotal;
+ if (linesAdded != 0)
+ multiLine = true;
+ if (step == steps - 1) {
+ modFlags |= SC_LASTSTEPINUNDOREDO;
+ if (multiLine)
+ modFlags |= SC_MULTILINEUNDOREDO;
+ }
+ NotifyModified(DocModification(modFlags, cellPosition, action.lenData,
+ linesAdded, action.data));
+ }
+
+ bool endSavePoint = cb.IsSavePoint();
+ if (startSavePoint != endSavePoint)
+ NotifySavePoint(endSavePoint);
+ }
+ enteredCount--;
+ }
+ return newPos;
+}
+
+int Document::Redo() {
+ int newPos = -1;
+ CheckReadOnly();
+ if (enteredCount == 0) {
+ enteredCount++;
+ if (!cb.IsReadOnly()) {
+ bool startSavePoint = cb.IsSavePoint();
+ bool multiLine = false;
+ int steps = cb.StartRedo();
+ for (int step = 0; step < steps; step++) {
+ const int prevLinesTotal = LinesTotal();
+ const Action &action = cb.GetRedoStep();
+ if (action.at == insertAction) {
+ NotifyModified(DocModification(
+ SC_MOD_BEFOREINSERT | SC_PERFORMED_REDO, action));
+ } else {
+ NotifyModified(DocModification(
+ SC_MOD_BEFOREDELETE | SC_PERFORMED_REDO, action));
+ }
+ cb.PerformRedoStep();
+ ModifiedAt(action.position);
+ newPos = action.position;
+
+ int modFlags = SC_PERFORMED_REDO;
+ if (action.at == insertAction) {
+ newPos += action.lenData;
+ modFlags |= SC_MOD_INSERTTEXT;
+ } else {
+ modFlags |= SC_MOD_DELETETEXT;
+ }
+ if (steps > 1)
+ modFlags |= SC_MULTISTEPUNDOREDO;
+ const int linesAdded = LinesTotal() - prevLinesTotal;
+ if (linesAdded != 0)
+ multiLine = true;
+ if (step == steps - 1) {
+ modFlags |= SC_LASTSTEPINUNDOREDO;
+ if (multiLine)
+ modFlags |= SC_MULTILINEUNDOREDO;
+ }
+ NotifyModified(
+ DocModification(modFlags, action.position, action.lenData,
+ linesAdded, action.data));
+ }
+
+ bool endSavePoint = cb.IsSavePoint();
+ if (startSavePoint != endSavePoint)
+ NotifySavePoint(endSavePoint);
+ }
+ enteredCount--;
+ }
+ return newPos;
+}
+
+/**
+ * Insert a single character.
+ */
+bool Document::InsertChar(int pos, char ch) {
+ char chs[2];
+ chs[0] = ch;
+ chs[1] = 0;
+ return InsertStyledString(pos*2, chs, 2);
+}
+
+/**
+ * Insert a null terminated string.
+ */
+bool Document::InsertString(int position, const char *s) {
+ return InsertString(position, s, strlen(s));
+}
+
+/**
+ * Insert a string with a length.
+ */
+bool Document::InsertString(int position, const char *s, size_t insertLength) {
+ bool changed = false;
+ if (insertLength > 0) {
+ char *sWithStyle = new char[insertLength * 2];
+ if (sWithStyle) {
+ for (size_t i = 0; i < insertLength; i++) {
+ sWithStyle[i*2] = s[i];
+ sWithStyle[i*2 + 1] = 0;
+ }
+ changed = InsertStyledString(position*2, sWithStyle,
+ static_cast<int>(insertLength*2));
+ delete []sWithStyle;
+ }
+ }
+ return changed;
+}
+
+void Document::ChangeChar(int pos, char ch) {
+ DeleteChars(pos, 1);
+ InsertChar(pos, ch);
+}
+
+void Document::DelChar(int pos) {
+ DeleteChars(pos, LenChar(pos));
+}
+
+void Document::DelCharBack(int pos) {
+ if (pos <= 0) {
+ return;
+ } else if (IsCrLf(pos - 2)) {
+ DeleteChars(pos - 2, 2);
+ } else if (dbcsCodePage) {
+ int startChar = MovePositionOutsideChar(pos - 1, -1, false);
+ DeleteChars(startChar, pos - startChar);
+ } else {
+ DeleteChars(pos - 1, 1);
+ }
+}
+
+static bool isindentchar(char ch) {
+ return (ch == ' ') || (ch == '\t');
+}
+
+static int NextTab(int pos, int tabSize) {
+ return ((pos / tabSize) + 1) * tabSize;
+}
+
+static void CreateIndentation(char *linebuf, int length, int indent, int tabSize, bool insertSpaces) {
+ length--; // ensure space for \0
+ if (!insertSpaces) {
+ while ((indent >= tabSize) && (length > 0)) {
+ *linebuf++ = '\t';
+ indent -= tabSize;
+ length--;
+ }
+ }
+ while ((indent > 0) && (length > 0)) {
+ *linebuf++ = ' ';
+ indent--;
+ length--;
+ }
+ *linebuf = '\0';
+}
+
+int Document::GetLineIndentation(int line) {
+ int indent = 0;
+ if ((line >= 0) && (line < LinesTotal())) {
+ int lineStart = LineStart(line);
+ int length = Length();
+ for (int i = lineStart;i < length;i++) {
+ char ch = cb.CharAt(i);
+ if (ch == ' ')
+ indent++;
+ else if (ch == '\t')
+ indent = NextTab(indent, tabInChars);
+ else
+ return indent;
+ }
+ }
+ return indent;
+}
+
+void Document::SetLineIndentation(int line, int indent) {
+ int indentOfLine = GetLineIndentation(line);
+ if (indent < 0)
+ indent = 0;
+ if (indent != indentOfLine) {
+ char linebuf[1000];
+ CreateIndentation(linebuf, sizeof(linebuf), indent, tabInChars, !useTabs);
+ int thisLineStart = LineStart(line);
+ int indentPos = GetLineIndentPosition(line);
+ BeginUndoAction();
+ DeleteChars(thisLineStart, indentPos - thisLineStart);
+ InsertString(thisLineStart, linebuf);
+ EndUndoAction();
+ }
+}
+
+int Document::GetLineIndentPosition(int line) {
+ if (line < 0)
+ return 0;
+ int pos = LineStart(line);
+ int length = Length();
+ while ((pos < length) && isindentchar(cb.CharAt(pos))) {
+ pos++;
+ }
+ return pos;
+}
+
+int Document::GetColumn(int pos) {
+ int column = 0;
+ int line = LineFromPosition(pos);
+ if ((line >= 0) && (line < LinesTotal())) {
+ for (int i = LineStart(line);i < pos;) {
+ char ch = cb.CharAt(i);
+ if (ch == '\t') {
+ column = NextTab(column, tabInChars);
+ i++;
+ } else if (ch == '\r') {
+ return column;
+ } else if (ch == '\n') {
+ return column;
+ } else {
+ column++;
+ i = MovePositionOutsideChar(i + 1, 1);
+ }
+ }
+ }
+ return column;
+}
+
+int Document::FindColumn(int line, int column) {
+ int position = LineStart(line);
+ int columnCurrent = 0;
+ if ((line >= 0) && (line < LinesTotal())) {
+ while ((columnCurrent < column) && (position < Length())) {
+ char ch = cb.CharAt(position);
+ if (ch == '\t') {
+ columnCurrent = NextTab(columnCurrent, tabInChars);
+ position++;
+ } else if (ch == '\r') {
+ return position;
+ } else if (ch == '\n') {
+ return position;
+ } else {
+ columnCurrent++;
+ position = MovePositionOutsideChar(position + 1, 1);
+ }
+ }
+ }
+ return position;
+}
+
+void Document::Indent(bool forwards, int lineBottom, int lineTop) {
+ // Dedent - suck white space off the front of the line to dedent by equivalent of a tab
+ for (int line = lineBottom; line >= lineTop; line--) {
+ int indentOfLine = GetLineIndentation(line);
+ if (forwards) {
+ if (LineStart(line) < LineEnd(line)) {
+ SetLineIndentation(line, indentOfLine + IndentSize());
+ }
+ } else {
+ SetLineIndentation(line, indentOfLine - IndentSize());
+ }
+ }
+}
+
+// Convert line endings for a piece of text to a particular mode.
+// Stop at len or when a NUL is found.
+// Caller must delete the returned pointer.
+char *Document::TransformLineEnds(int *pLenOut, const char *s, size_t len, int eolMode) {
+ char *dest = new char[2 * len + 1];
+ const char *sptr = s;
+ char *dptr = dest;
+ for (size_t i = 0; (i < len) && (*sptr != '\0'); i++) {
+ if (*sptr == '\n' || *sptr == '\r') {
+ if (eolMode == SC_EOL_CR) {
+ *dptr++ = '\r';
+ } else if (eolMode == SC_EOL_LF) {
+ *dptr++ = '\n';
+ } else { // eolMode == SC_EOL_CRLF
+ *dptr++ = '\r';
+ *dptr++ = '\n';
+ }
+ if ((*sptr == '\r') && (i+1 < len) && (*(sptr+1) == '\n')) {
+ i++;
+ sptr++;
+ }
+ sptr++;
+ } else {
+ *dptr++ = *sptr++;
+ }
+ }
+ *dptr++ = '\0';
+ *pLenOut = (dptr - dest) - 1;
+ return dest;
+}
+
+void Document::ConvertLineEnds(int eolModeSet) {
+ BeginUndoAction();
+
+ for (int pos = 0; pos < Length(); pos++) {
+ if (cb.CharAt(pos) == '\r') {
+ if (cb.CharAt(pos + 1) == '\n') {
+ // CRLF
+ if (eolModeSet == SC_EOL_CR) {
+ DeleteChars(pos + 1, 1); // Delete the LF
+ } else if (eolModeSet == SC_EOL_LF) {
+ DeleteChars(pos, 1); // Delete the CR
+ } else {
+ pos++;
+ }
+ } else {
+ // CR
+ if (eolModeSet == SC_EOL_CRLF) {
+ InsertString(pos + 1, "\n", 1); // Insert LF
+ pos++;
+ } else if (eolModeSet == SC_EOL_LF) {
+ InsertString(pos, "\n", 1); // Insert LF
+ DeleteChars(pos + 1, 1); // Delete CR
+ }
+ }
+ } else if (cb.CharAt(pos) == '\n') {
+ // LF
+ if (eolModeSet == SC_EOL_CRLF) {
+ InsertString(pos, "\r", 1); // Insert CR
+ pos++;
+ } else if (eolModeSet == SC_EOL_CR) {
+ InsertString(pos, "\r", 1); // Insert CR
+ DeleteChars(pos + 1, 1); // Delete LF
+ }
+ }
+ }
+
+ EndUndoAction();
+}
+
+bool Document::IsWhiteLine(int line) {
+ int currentChar = LineStart(line);
+ int endLine = LineEnd(line);
+ while (currentChar < endLine) {
+ if (cb.CharAt(currentChar) != ' ' && cb.CharAt(currentChar) != '\t') {
+ return false;
+ }
+ ++currentChar;
+ }
+ return true;
+}
+
+int Document::ParaUp(int pos) {
+ int line = LineFromPosition(pos);
+ line--;
+ while (line >= 0 && IsWhiteLine(line)) { // skip empty lines
+ line--;
+ }
+ while (line >= 0 && !IsWhiteLine(line)) { // skip non-empty lines
+ line--;
+ }
+ line++;
+ return LineStart(line);
+}
+
+int Document::ParaDown(int pos) {
+ int line = LineFromPosition(pos);
+ while (line < LinesTotal() && !IsWhiteLine(line)) { // skip non-empty lines
+ line++;
+ }
+ while (line < LinesTotal() && IsWhiteLine(line)) { // skip empty lines
+ line++;
+ }
+ if (line < LinesTotal())
+ return LineStart(line);
+ else // end of a document
+ return LineEnd(line-1);
+}
+
+CharClassify::cc Document::WordCharClass(unsigned char ch) {
+ if ((SC_CP_UTF8 == dbcsCodePage) && (ch >= 0x80))
+ return CharClassify::ccWord;
+ return charClass.GetClass(ch);
+}
+
+/**
+ * Used by commmands that want to select whole words.
+ * Finds the start of word at pos when delta < 0 or the end of the word when delta >= 0.
+ */
+int Document::ExtendWordSelect(int pos, int delta, bool onlyWordCharacters) {
+ CharClassify::cc ccStart = CharClassify::ccWord;
+ if (delta < 0) {
+ if (!onlyWordCharacters)
+ ccStart = WordCharClass(cb.CharAt(pos-1));
+ while (pos > 0 && (WordCharClass(cb.CharAt(pos - 1)) == ccStart))
+ pos--;
+ } else {
+ if (!onlyWordCharacters)
+ ccStart = WordCharClass(cb.CharAt(pos));
+ while (pos < (Length()) && (WordCharClass(cb.CharAt(pos)) == ccStart))
+ pos++;
+ }
+ return MovePositionOutsideChar(pos, delta);
+}
+
+/**
+ * Find the start of the next word in either a forward (delta >= 0) or backwards direction
+ * (delta < 0).
+ * This is looking for a transition between character classes although there is also some
+ * additional movement to transit white space.
+ * Used by cursor movement by word commands.
+ */
+int Document::NextWordStart(int pos, int delta) {
+ if (delta < 0) {
+ while (pos > 0 && (WordCharClass(cb.CharAt(pos - 1)) == CharClassify::ccSpace))
+ pos--;
+ if (pos > 0) {
+ CharClassify::cc ccStart = WordCharClass(cb.CharAt(pos-1));
+ while (pos > 0 && (WordCharClass(cb.CharAt(pos - 1)) == ccStart)) {
+ pos--;
+ }
+ }
+ } else {
+ CharClassify::cc ccStart = WordCharClass(cb.CharAt(pos));
+ while (pos < (Length()) && (WordCharClass(cb.CharAt(pos)) == ccStart))
+ pos++;
+ while (pos < (Length()) && (WordCharClass(cb.CharAt(pos)) == CharClassify::ccSpace))
+ pos++;
+ }
+ return pos;
+}
+
+/**
+ * Find the end of the next word in either a forward (delta >= 0) or backwards direction
+ * (delta < 0).
+ * This is looking for a transition between character classes although there is also some
+ * additional movement to transit white space.
+ * Used by cursor movement by word commands.
+ */
+int Document::NextWordEnd(int pos, int delta) {
+ if (delta < 0) {
+ if (pos > 0) {
+ CharClassify::cc ccStart = WordCharClass(cb.CharAt(pos-1));
+ if (ccStart != CharClassify::ccSpace) {
+ while (pos > 0 && WordCharClass(cb.CharAt(pos - 1)) == ccStart) {
+ pos--;
+ }
+ }
+ while (pos > 0 && WordCharClass(cb.CharAt(pos - 1)) == CharClassify::ccSpace) {
+ pos--;
+ }
+ }
+ } else {
+ while (pos < Length() && WordCharClass(cb.CharAt(pos)) == CharClassify::ccSpace) {
+ pos++;
+ }
+ if (pos < Length()) {
+ CharClassify::cc ccStart = WordCharClass(cb.CharAt(pos));
+ while (pos < Length() && WordCharClass(cb.CharAt(pos)) == ccStart) {
+ pos++;
+ }
+ }
+ }
+ return pos;
+}
+
+/**
+ * Check that the character at the given position is a word or punctuation character and that
+ * the previous character is of a different character class.
+ */
+bool Document::IsWordStartAt(int pos) {
+ if (pos > 0) {
+ CharClassify::cc ccPos = WordCharClass(CharAt(pos));
+ return (ccPos == CharClassify::ccWord || ccPos == CharClassify::ccPunctuation) &&
+ (ccPos != WordCharClass(CharAt(pos - 1)));
+ }
+ return true;
+}
+
+/**
+ * Check that the character at the given position is a word or punctuation character and that
+ * the next character is of a different character class.
+ */
+bool Document::IsWordEndAt(int pos) {
+ if (pos < Length()) {
+ CharClassify::cc ccPrev = WordCharClass(CharAt(pos-1));
+ return (ccPrev == CharClassify::ccWord || ccPrev == CharClassify::ccPunctuation) &&
+ (ccPrev != WordCharClass(CharAt(pos)));
+ }
+ return true;
+}
+
+/**
+ * Check that the given range is has transitions between character classes at both
+ * ends and where the characters on the inside are word or punctuation characters.
+ */
+bool Document::IsWordAt(int start, int end) {
+ return IsWordStartAt(start) && IsWordEndAt(end);
+}
+
+// The comparison and case changing functions here assume ASCII
+// or extended ASCII such as the normal Windows code page.
+
+static inline char MakeUpperCase(char ch) {
+ if (ch < 'a' || ch > 'z')
+ return ch;
+ else
+ return static_cast<char>(ch - 'a' + 'A');
+}
+
+static inline char MakeLowerCase(char ch) {
+ if (ch < 'A' || ch > 'Z')
+ return ch;
+ else
+ return static_cast<char>(ch - 'A' + 'a');
+}
+
+// Define a way for the Regular Expression code to access the document
+class DocumentIndexer : public CharacterIndexer {
+ Document *pdoc;
+ int end;
+public:
+ DocumentIndexer(Document *pdoc_, int end_) :
+ pdoc(pdoc_), end(end_) {
+ }
+
+ virtual ~DocumentIndexer() {
+ }
+
+ virtual char CharAt(int index) {
+ if (index < 0 || index >= end)
+ return 0;
+ else
+ return pdoc->CharAt(index);
+ }
+};
+
+/**
+ * Find text in document, supporting both forward and backward
+ * searches (just pass minPos > maxPos to do a backward search)
+ * Has not been tested with backwards DBCS searches yet.
+ */
+long Document::FindText(int minPos, int maxPos, const char *s,
+ bool caseSensitive, bool word, bool wordStart, bool regExp, bool posix,
+ int *length) {
+ if (regExp) {
+ if (!pre)
+ pre = new RESearch(&charClass);
+ if (!pre)
+ return -1;
+
+ int increment = (minPos <= maxPos) ? 1 : -1;
+
+ int startPos = minPos;
+ int endPos = maxPos;
+
+ // Range endpoints should not be inside DBCS characters, but just in case, move them.
+ startPos = MovePositionOutsideChar(startPos, 1, false);
+ endPos = MovePositionOutsideChar(endPos, 1, false);
+
+ const char *errmsg = pre->Compile(s, *length, caseSensitive, posix);
+ if (errmsg) {
+ return -1;
+ }
+ // Find a variable in a property file: \$(\([A-Za-z0-9_.]+\))
+ // Replace first '.' with '-' in each property file variable reference:
+ // Search: \$(\([A-Za-z0-9_-]+\)\.\([A-Za-z0-9_.]+\))
+ // Replace: $(\1-\2)
+ int lineRangeStart = LineFromPosition(startPos);
+ int lineRangeEnd = LineFromPosition(endPos);
+ if ((increment == 1) &&
+ (startPos >= LineEnd(lineRangeStart)) &&
+ (lineRangeStart < lineRangeEnd)) {
+ // the start position is at end of line or between line end characters.
+ lineRangeStart++;
+ startPos = LineStart(lineRangeStart);
+ }
+ int pos = -1;
+ int lenRet = 0;
+ char searchEnd = s[*length - 1];
+ int lineRangeBreak = lineRangeEnd + increment;
+ for (int line = lineRangeStart; line != lineRangeBreak; line += increment) {
+ int startOfLine = LineStart(line);
+ int endOfLine = LineEnd(line);
+ if (increment == 1) {
+ if (line == lineRangeStart) {
+ if ((startPos != startOfLine) && (s[0] == '^'))
+ continue; // Can't match start of line if start position after start of line
+ startOfLine = startPos;
+ }
+ if (line == lineRangeEnd) {
+ if ((endPos != endOfLine) && (searchEnd == '$'))
+ continue; // Can't match end of line if end position before end of line
+ endOfLine = endPos;
+ }
+ } else {
+ if (line == lineRangeEnd) {
+ if ((endPos != startOfLine) && (s[0] == '^'))
+ continue; // Can't match start of line if end position after start of line
+ startOfLine = endPos;
+ }
+ if (line == lineRangeStart) {
+ if ((startPos != endOfLine) && (searchEnd == '$'))
+ continue; // Can't match end of line if start position before end of line
+ endOfLine = startPos;
+ }
+ }
+
+ DocumentIndexer di(this, endOfLine);
+ int success = pre->Execute(di, startOfLine, endOfLine);
+ if (success) {
+ pos = pre->bopat[0];
+ lenRet = pre->eopat[0] - pre->bopat[0];
+ if (increment == -1) {
+ // Check for the last match on this line.
+ int repetitions = 1000; // Break out of infinite loop
+ while (success && (pre->eopat[0] <= endOfLine) && (repetitions--)) {
+ success = pre->Execute(di, pos+1, endOfLine);
+ if (success) {
+ if (pre->eopat[0] <= minPos) {
+ pos = pre->bopat[0];
+ lenRet = pre->eopat[0] - pre->bopat[0];
+ } else {
+ success = 0;
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ *length = lenRet;
+ return pos;
+
+ } else {
+
+ bool forward = minPos <= maxPos;
+ int increment = forward ? 1 : -1;
+
+ // Range endpoints should not be inside DBCS characters, but just in case, move them.
+ int startPos = MovePositionOutsideChar(minPos, increment, false);
+ int endPos = MovePositionOutsideChar(maxPos, increment, false);
+
+ // Compute actual search ranges needed
+ int lengthFind = *length;
+ if (lengthFind == -1)
+ lengthFind = static_cast<int>(strlen(s));
+ int endSearch = endPos;
+ if (startPos <= endPos) {
+ endSearch = endPos - lengthFind + 1;
+ }
+ //Platform::DebugPrintf("Find %d %d %s %d\n", startPos, endPos, ft->lpstrText, lengthFind);
+ char firstChar = s[0];
+ if (!caseSensitive)
+ firstChar = static_cast<char>(MakeUpperCase(firstChar));
+ int pos = forward ? startPos : (startPos - 1);
+ while (forward ? (pos < endSearch) : (pos >= endSearch)) {
+ char ch = CharAt(pos);
+ if (caseSensitive) {
+ if (ch == firstChar) {
+ bool found = true;
+ if (pos + lengthFind > Platform::Maximum(startPos, endPos)) found = false;
+ for (int posMatch = 1; posMatch < lengthFind && found; posMatch++) {
+ ch = CharAt(pos + posMatch);
+ if (ch != s[posMatch])
+ found = false;
+ }
+ if (found) {
+ if ((!word && !wordStart) ||
+ word && IsWordAt(pos, pos + lengthFind) ||
+ wordStart && IsWordStartAt(pos))
+ return pos;
+ }
+ }
+ } else {
+ if (MakeUpperCase(ch) == firstChar) {
+ bool found = true;
+ if (pos + lengthFind > Platform::Maximum(startPos, endPos)) found = false;
+ for (int posMatch = 1; posMatch < lengthFind && found; posMatch++) {
+ ch = CharAt(pos + posMatch);
+ if (MakeUpperCase(ch) != MakeUpperCase(s[posMatch]))
+ found = false;
+ }
+ if (found) {
+ if ((!word && !wordStart) ||
+ word && IsWordAt(pos, pos + lengthFind) ||
+ wordStart && IsWordStartAt(pos))
+ return pos;
+ }
+ }
+ }
+ pos += increment;
+ if (dbcsCodePage && (pos >= 0)) {
+ // Ensure trying to match from start of character
+ pos = MovePositionOutsideChar(pos, increment, false);
+ }
+ }
+ }
+ //Platform::DebugPrintf("Not found\n");
+ return -1;
+}
+
+const char *Document::SubstituteByPosition(const char *text, int *length) {
+ if (!pre)
+ return 0;
+ delete []substituted;
+ substituted = 0;
+ DocumentIndexer di(this, Length());
+ if (!pre->GrabMatches(di))
+ return 0;
+ unsigned int lenResult = 0;
+ for (int i = 0; i < *length; i++) {
+ if (text[i] == '\\') {
+ if (text[i + 1] >= '1' && text[i + 1] <= '9') {
+ unsigned int patNum = text[i + 1] - '0';
+ lenResult += pre->eopat[patNum] - pre->bopat[patNum];
+ i++;
+ } else {
+ switch (text[i + 1]) {
+ case 'a':
+ case 'b':
+ case 'f':
+ case 'n':
+ case 'r':
+ case 't':
+ case 'v':
+ i++;
+ }
+ lenResult++;
+ }
+ } else {
+ lenResult++;
+ }
+ }
+ substituted = new char[lenResult + 1];
+ if (!substituted)
+ return 0;
+ char *o = substituted;
+ for (int j = 0; j < *length; j++) {
+ if (text[j] == '\\') {
+ if (text[j + 1] >= '1' && text[j + 1] <= '9') {
+ unsigned int patNum = text[j + 1] - '0';
+ unsigned int len = pre->eopat[patNum] - pre->bopat[patNum];
+ if (pre->pat[patNum]) // Will be null if try for a match that did not occur
+ memcpy(o, pre->pat[patNum], len);
+ o += len;
+ j++;
+ } else {
+ j++;
+ switch (text[j]) {
+ case 'a':
+ *o++ = '\a';
+ break;
+ case 'b':
+ *o++ = '\b';
+ break;
+ case 'f':
+ *o++ = '\f';
+ break;
+ case 'n':
+ *o++ = '\n';
+ break;
+ case 'r':
+ *o++ = '\r';
+ break;
+ case 't':
+ *o++ = '\t';
+ break;
+ case 'v':
+ *o++ = '\v';
+ break;
+ default:
+ *o++ = '\\';
+ j--;
+ }
+ }
+ } else {
+ *o++ = text[j];
+ }
+ }
+ *o = '\0';
+ *length = lenResult;
+ return substituted;
+}
+
+int Document::LinesTotal() {
+ return cb.Lines();
+}
+
+void Document::ChangeCase(Range r, bool makeUpperCase) {
+ for (int pos = r.start; pos < r.end;) {
+ int len = LenChar(pos);
+ if (len == 1) {
+ char ch = CharAt(pos);
+ if (makeUpperCase) {
+ if (IsLowerCase(ch)) {
+ ChangeChar(pos, static_cast<char>(MakeUpperCase(ch)));
+ }
+ } else {
+ if (IsUpperCase(ch)) {
+ ChangeChar(pos, static_cast<char>(MakeLowerCase(ch)));
+ }
+ }
+ }
+ pos += len;
+ }
+}
+
+void Document::SetDefaultCharClasses(bool includeWordClass) {
+ charClass.SetDefaultCharClasses(includeWordClass);
+}
+
+void Document::SetCharClasses(const unsigned char *chars, CharClassify::cc newCharClass) {
+ charClass.SetCharClasses(chars, newCharClass);
+}
+
+void Document::SetStylingBits(int bits) {
+ stylingBits = bits;
+ stylingBitsMask = 0;
+ for (int bit = 0; bit < stylingBits; bit++) {
+ stylingBitsMask <<= 1;
+ stylingBitsMask |= 1;
+ }
+}
+
+void Document::StartStyling(int position, char mask) {
+ stylingMask = mask;
+ endStyled = position;
+}
+
+bool Document::SetStyleFor(int length, char style) {
+ if (enteredCount != 0) {
+ return false;
+ } else {
+ enteredCount++;
+ style &= stylingMask;
+ int prevEndStyled = endStyled;
+ if (cb.SetStyleFor(endStyled, length, style, stylingMask)) {
+ DocModification mh(SC_MOD_CHANGESTYLE | SC_PERFORMED_USER,
+ prevEndStyled, length);
+ NotifyModified(mh);
+ }
+ endStyled += length;
+ enteredCount--;
+ return true;
+ }
+}
+
+bool Document::SetStyles(int length, char *styles) {
+ if (enteredCount != 0) {
+ return false;
+ } else {
+ enteredCount++;
+ bool didChange = false;
+ int startMod = 0;
+ int endMod = 0;
+ for (int iPos = 0; iPos < length; iPos++, endStyled++) {
+ PLATFORM_ASSERT(endStyled < Length());
+ if (cb.SetStyleAt(endStyled, styles[iPos], stylingMask)) {
+ if (!didChange) {
+ startMod = endStyled;
+ }
+ didChange = true;
+ endMod = endStyled;
+ }
+ }
+ if (didChange) {
+ DocModification mh(SC_MOD_CHANGESTYLE | SC_PERFORMED_USER,
+ startMod, endMod - startMod + 1);
+ NotifyModified(mh);
+ }
+ enteredCount--;
+ return true;
+ }
+}
+
+bool Document::EnsureStyledTo(int pos) {
+ if (pos > GetEndStyled()) {
+ IncrementStyleClock();
+ // Ask the watchers to style, and stop as soon as one responds.
+ for (int i = 0; pos > GetEndStyled() && i < lenWatchers; i++) {
+ watchers[i].watcher->NotifyStyleNeeded(this, watchers[i].userData, pos);
+ }
+ }
+ return pos <= GetEndStyled();
+}
+
+void Document::IncrementStyleClock() {
+ styleClock++;
+ if (styleClock > 0x100000) {
+ styleClock = 0;
+ }
+}
+
+bool Document::AddWatcher(DocWatcher *watcher, void *userData) {
+ for (int i = 0; i < lenWatchers; i++) {
+ if ((watchers[i].watcher == watcher) &&
+ (watchers[i].userData == userData))
+ return false;
+ }
+ WatcherWithUserData *pwNew = new WatcherWithUserData[lenWatchers + 1];
+ if (!pwNew)
+ return false;
+ for (int j = 0; j < lenWatchers; j++)
+ pwNew[j] = watchers[j];
+ pwNew[lenWatchers].watcher = watcher;
+ pwNew[lenWatchers].userData = userData;
+ delete []watchers;
+ watchers = pwNew;
+ lenWatchers++;
+ return true;
+}
+
+bool Document::RemoveWatcher(DocWatcher *watcher, void *userData) {
+ for (int i = 0; i < lenWatchers; i++) {
+ if ((watchers[i].watcher == watcher) &&
+ (watchers[i].userData == userData)) {
+ if (lenWatchers == 1) {
+ delete []watchers;
+ watchers = 0;
+ lenWatchers = 0;
+ } else {
+ WatcherWithUserData *pwNew = new WatcherWithUserData[lenWatchers];
+ if (!pwNew)
+ return false;
+ for (int j = 0; j < lenWatchers - 1; j++) {
+ pwNew[j] = (j < i) ? watchers[j] : watchers[j + 1];
+ }
+ delete []watchers;
+ watchers = pwNew;
+ lenWatchers--;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+void Document::NotifyModifyAttempt() {
+ for (int i = 0; i < lenWatchers; i++) {
+ watchers[i].watcher->NotifyModifyAttempt(this, watchers[i].userData);
+ }
+}
+
+void Document::NotifySavePoint(bool atSavePoint) {
+ for (int i = 0; i < lenWatchers; i++) {
+ watchers[i].watcher->NotifySavePoint(this, watchers[i].userData, atSavePoint);
+ }
+}
+
+void Document::NotifyModified(DocModification mh) {
+ for (int i = 0; i < lenWatchers; i++) {
+ watchers[i].watcher->NotifyModified(this, mh, watchers[i].userData);
+ }
+}
+
+bool Document::IsWordPartSeparator(char ch) {
+ return (WordCharClass(ch) == CharClassify::ccWord) && IsPunctuation(ch);
+}
+
+int Document::WordPartLeft(int pos) {
+ if (pos > 0) {
+ --pos;
+ char startChar = cb.CharAt(pos);
+ if (IsWordPartSeparator(startChar)) {
+ while (pos > 0 && IsWordPartSeparator(cb.CharAt(pos))) {
+ --pos;
+ }
+ }
+ if (pos > 0) {
+ startChar = cb.CharAt(pos);
+ --pos;
+ if (IsLowerCase(startChar)) {
+ while (pos > 0 && IsLowerCase(cb.CharAt(pos)))
+ --pos;
+ if (!IsUpperCase(cb.CharAt(pos)) && !IsLowerCase(cb.CharAt(pos)))
+ ++pos;
+ } else if (IsUpperCase(startChar)) {
+ while (pos > 0 && IsUpperCase(cb.CharAt(pos)))
+ --pos;
+ if (!IsUpperCase(cb.CharAt(pos)))
+ ++pos;
+ } else if (IsADigit(startChar)) {
+ while (pos > 0 && IsADigit(cb.CharAt(pos)))
+ --pos;
+ if (!IsADigit(cb.CharAt(pos)))
+ ++pos;
+ } else if (IsPunctuation(startChar)) {
+ while (pos > 0 && IsPunctuation(cb.CharAt(pos)))
+ --pos;
+ if (!IsPunctuation(cb.CharAt(pos)))
+ ++pos;
+ } else if (isspacechar(startChar)) {
+ while (pos > 0 && isspacechar(cb.CharAt(pos)))
+ --pos;
+ if (!isspacechar(cb.CharAt(pos)))
+ ++pos;
+ } else if (!isascii(startChar)) {
+ while (pos > 0 && !isascii(cb.CharAt(pos)))
+ --pos;
+ if (isascii(cb.CharAt(pos)))
+ ++pos;
+ } else {
+ ++pos;
+ }
+ }
+ }
+ return pos;
+}
+
+int Document::WordPartRight(int pos) {
+ char startChar = cb.CharAt(pos);
+ int length = Length();
+ if (IsWordPartSeparator(startChar)) {
+ while (pos < length && IsWordPartSeparator(cb.CharAt(pos)))
+ ++pos;
+ startChar = cb.CharAt(pos);
+ }
+ if (!isascii(startChar)) {
+ while (pos < length && !isascii(cb.CharAt(pos)))
+ ++pos;
+ } else if (IsLowerCase(startChar)) {
+ while (pos < length && IsLowerCase(cb.CharAt(pos)))
+ ++pos;
+ } else if (IsUpperCase(startChar)) {
+ if (IsLowerCase(cb.CharAt(pos + 1))) {
+ ++pos;
+ while (pos < length && IsLowerCase(cb.CharAt(pos)))
+ ++pos;
+ } else {
+ while (pos < length && IsUpperCase(cb.CharAt(pos)))
+ ++pos;
+ }
+ if (IsLowerCase(cb.CharAt(pos)) && IsUpperCase(cb.CharAt(pos - 1)))
+ --pos;
+ } else if (IsADigit(startChar)) {
+ while (pos < length && IsADigit(cb.CharAt(pos)))
+ ++pos;
+ } else if (IsPunctuation(startChar)) {
+ while (pos < length && IsPunctuation(cb.CharAt(pos)))
+ ++pos;
+ } else if (isspacechar(startChar)) {
+ while (pos < length && isspacechar(cb.CharAt(pos)))
+ ++pos;
+ } else {
+ ++pos;
+ }
+ return pos;
+}
+
+bool IsLineEndChar(char c) {
+ return (c == '\n' || c == '\r');
+}
+
+int Document::ExtendStyleRange(int pos, int delta, bool singleLine) {
+ int sStart = cb.StyleAt(pos);
+ if (delta < 0) {
+ while (pos > 0 && (cb.StyleAt(pos) == sStart) && (!singleLine || !IsLineEndChar(cb.CharAt(pos))) )
+ pos--;
+ pos++;
+ } else {
+ while (pos < (Length()) && (cb.StyleAt(pos) == sStart) && (!singleLine || !IsLineEndChar(cb.CharAt(pos))) )
+ pos++;
+ }
+ return pos;
+}
+
+static char BraceOpposite(char ch) {
+ switch (ch) {
+ case '(':
+ return ')';
+ case ')':
+ return '(';
+ case '[':
+ return ']';
+ case ']':
+ return '[';
+ case '{':
+ return '}';
+ case '}':
+ return '{';
+ case '<':
+ return '>';
+ case '>':
+ return '<';
+ default:
+ return '\0';
+ }
+}
+
+// TODO: should be able to extend styled region to find matching brace
+int Document::BraceMatch(int position, int /*maxReStyle*/) {
+ char chBrace = CharAt(position);
+ char chSeek = BraceOpposite(chBrace);
+ if (chSeek == '\0')
+ return - 1;
+ char styBrace = static_cast<char>(StyleAt(position) & stylingBitsMask);
+ int direction = -1;
+ if (chBrace == '(' || chBrace == '[' || chBrace == '{' || chBrace == '<')
+ direction = 1;
+ int depth = 1;
+ position = position + direction;
+ while ((position >= 0) && (position < Length())) {
+ position = MovePositionOutsideChar(position, direction);
+ char chAtPos = CharAt(position);
+ char styAtPos = static_cast<char>(StyleAt(position) & stylingBitsMask);
+ if ((position > GetEndStyled()) || (styAtPos == styBrace)) {
+ if (chAtPos == chBrace)
+ depth++;
+ if (chAtPos == chSeek)
+ depth--;
+ if (depth == 0)
+ return position;
+ }
+ position = position + direction;
+ }
+ return - 1;
+}
diff --git a/src/Document.h b/src/Document.h
new file mode 100755
index 0000000..d774d56
--- /dev/null
+++ b/src/Document.h
@@ -0,0 +1,305 @@
+// Scintilla source code edit control
+/** @file Document.h
+ ** Text document that handles notifications, DBCS, styling, words and end of line.
+ **/
+// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef DOCUMENT_H
+#define DOCUMENT_H
+
+/**
+ * A Position is a position within a document between two characters or at the beginning or end.
+ * Sometimes used as a character index where it identifies the character after the position.
+ */
+typedef int Position;
+const Position invalidPosition = -1;
+
+/**
+ * The range class represents a range of text in a document.
+ * The two values are not sorted as one end may be more significant than the other
+ * as is the case for the selection where the end position is the position of the caret.
+ * If either position is invalidPosition then the range is invalid and most operations will fail.
+ */
+class Range {
+public:
+ Position start;
+ Position end;
+
+ Range(Position pos=0) :
+ start(pos), end(pos) {
+ };
+ Range(Position start_, Position end_) :
+ start(start_), end(end_) {
+ };
+
+ bool Valid() const {
+ return (start != invalidPosition) && (end != invalidPosition);
+ }
+
+ // Is the position within the range?
+ bool Contains(Position pos) const {
+ if (start < end) {
+ return (pos >= start && pos <= end);
+ } else {
+ return (pos <= start && pos >= end);
+ }
+ }
+
+ // Is the character after pos within the range?
+ bool ContainsCharacter(Position pos) const {
+ if (start < end) {
+ return (pos >= start && pos < end);
+ } else {
+ return (pos < start && pos >= end);
+ }
+ }
+
+ bool Contains(Range other) const {
+ return Contains(other.start) && Contains(other.end);
+ }
+
+ bool Overlaps(Range other) const {
+ return
+ Contains(other.start) ||
+ Contains(other.end) ||
+ other.Contains(start) ||
+ other.Contains(end);
+ }
+};
+
+class DocWatcher;
+class DocModification;
+class RESearch;
+
+/**
+ */
+class Document {
+
+public:
+ /** Used to pair watcher pointer with user data. */
+ class WatcherWithUserData {
+ public:
+ DocWatcher *watcher;
+ void *userData;
+ WatcherWithUserData() {
+ watcher = 0;
+ userData = 0;
+ }
+ };
+
+ enum charClassification { ccSpace, ccNewLine, ccWord, ccPunctuation };
+
+private:
+ int refCount;
+ CellBuffer cb;
+ CharClassify charClass;
+ char stylingMask;
+ int endStyled;
+ int styleClock;
+ int enteredCount;
+ int enteredReadOnlyCount;
+
+ WatcherWithUserData *watchers;
+ int lenWatchers;
+
+ bool matchesValid;
+ RESearch *pre;
+ char *substituted;
+
+public:
+ int stylingBits;
+ int stylingBitsMask;
+
+ int eolMode;
+ /// Can also be SC_CP_UTF8 to enable UTF-8 mode
+ int dbcsCodePage;
+ int tabInChars;
+ int indentInChars;
+ int actualIndentInChars;
+ bool useTabs;
+ bool tabIndents;
+ bool backspaceUnindents;
+
+ Document();
+ virtual ~Document();
+
+ int AddRef();
+ int Release();
+
+ int LineFromPosition(int pos);
+ int ClampPositionIntoDocument(int pos);
+ bool IsCrLf(int pos);
+ int LenChar(int pos);
+ int MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd=true);
+
+ // Gateways to modifying document
+ void ModifiedAt(int pos);
+ bool DeleteChars(int pos, int len);
+ bool InsertStyledString(int position, char *s, int insertLength);
+ int Undo();
+ int Redo();
+ bool CanUndo() { return cb.CanUndo(); }
+ bool CanRedo() { return cb.CanRedo(); }
+ void DeleteUndoHistory() { cb.DeleteUndoHistory(); }
+ bool SetUndoCollection(bool collectUndo) {
+ return cb.SetUndoCollection(collectUndo);
+ }
+ bool IsCollectingUndo() { return cb.IsCollectingUndo(); }
+ void BeginUndoAction() { cb.BeginUndoAction(); }
+ void EndUndoAction() { cb.EndUndoAction(); }
+ void SetSavePoint();
+ bool IsSavePoint() { return cb.IsSavePoint(); }
+
+ int GetLineIndentation(int line);
+ void SetLineIndentation(int line, int indent);
+ int GetLineIndentPosition(int line);
+ int GetColumn(int position);
+ int FindColumn(int line, int column);
+ void Indent(bool forwards, int lineBottom, int lineTop);
+ static char *TransformLineEnds(int *pLenOut, const char *s, size_t len, int eolMode);
+ void ConvertLineEnds(int eolModeSet);
+ void SetReadOnly(bool set) { cb.SetReadOnly(set); }
+ bool IsReadOnly() { return cb.IsReadOnly(); }
+
+ bool InsertChar(int pos, char ch);
+ bool InsertString(int position, const char *s);
+ bool InsertString(int position, const char *s, size_t insertLength);
+ void ChangeChar(int pos, char ch);
+ void DelChar(int pos);
+ void DelCharBack(int pos);
+
+ char CharAt(int position) { return cb.CharAt(position); }
+ void GetCharRange(char *buffer, int position, int lengthRetrieve) {
+ cb.GetCharRange(buffer, position, lengthRetrieve);
+ }
+ char StyleAt(int position) { return cb.StyleAt(position); }
+ int GetMark(int line) { return cb.GetMark(line); }
+ int AddMark(int line, int markerNum);
+ void AddMarkSet(int line, int valueSet);
+ void DeleteMark(int line, int markerNum);
+ void DeleteMarkFromHandle(int markerHandle);
+ void DeleteAllMarks(int markerNum);
+ int LineFromHandle(int markerHandle) { return cb.LineFromHandle(markerHandle); }
+ int LineStart(int line);
+ int LineEnd(int line);
+ int LineEndPosition(int position);
+ int VCHomePosition(int position);
+
+ int SetLevel(int line, int level);
+ int GetLevel(int line) { return cb.GetLevel(line); }
+ void ClearLevels() { cb.ClearLevels(); }
+ int GetLastChild(int lineParent, int level=-1);
+ int GetFoldParent(int line);
+
+ void Indent(bool forwards);
+ int ExtendWordSelect(int pos, int delta, bool onlyWordCharacters=false);
+ int NextWordStart(int pos, int delta);
+ int NextWordEnd(int pos, int delta);
+ int Length() { return cb.Length(); }
+ void Allocate(int newSize) { cb.Allocate(newSize*2); }
+ long FindText(int minPos, int maxPos, const char *s,
+ bool caseSensitive, bool word, bool wordStart, bool regExp, bool posix, int *length);
+ long FindText(int iMessage, unsigned long wParam, long lParam);
+ const char *SubstituteByPosition(const char *text, int *length);
+ int LinesTotal();
+
+ void ChangeCase(Range r, bool makeUpperCase);
+
+ void SetDefaultCharClasses(bool includeWordClass);
+ void SetCharClasses(const unsigned char *chars, CharClassify::cc newCharClass);
+ void SetStylingBits(int bits);
+ void StartStyling(int position, char mask);
+ bool SetStyleFor(int length, char style);
+ bool SetStyles(int length, char *styles);
+ int GetEndStyled() { return endStyled; }
+ bool EnsureStyledTo(int pos);
+ int GetStyleClock() { return styleClock; }
+ void IncrementStyleClock();
+
+ int SetLineState(int line, int state) { return cb.SetLineState(line, state); }
+ int GetLineState(int line) { return cb.GetLineState(line); }
+ int GetMaxLineState() { return cb.GetMaxLineState(); }
+
+ bool AddWatcher(DocWatcher *watcher, void *userData);
+ bool RemoveWatcher(DocWatcher *watcher, void *userData);
+ const WatcherWithUserData *GetWatchers() const { return watchers; }
+ int GetLenWatchers() const { return lenWatchers; }
+
+ bool IsWordPartSeparator(char ch);
+ int WordPartLeft(int pos);
+ int WordPartRight(int pos);
+ int ExtendStyleRange(int pos, int delta, bool singleLine = false);
+ bool IsWhiteLine(int line);
+ int ParaUp(int pos);
+ int ParaDown(int pos);
+ int IndentSize() { return actualIndentInChars; }
+ int BraceMatch(int position, int maxReStyle);
+
+private:
+ void CheckReadOnly();
+
+ CharClassify::cc WordCharClass(unsigned char ch);
+ bool IsWordStartAt(int pos);
+ bool IsWordEndAt(int pos);
+ bool IsWordAt(int start, int end);
+
+ void NotifyModifyAttempt();
+ void NotifySavePoint(bool atSavePoint);
+ void NotifyModified(DocModification mh);
+};
+
+/**
+ * To optimise processing of document modifications by DocWatchers, a hint is passed indicating the
+ * scope of the change.
+ * If the DocWatcher is a document view then this can be used to optimise screen updating.
+ */
+class DocModification {
+public:
+ int modificationType;
+ int position;
+ int length;
+ int linesAdded; /**< Negative if lines deleted. */
+ const char *text; /**< Only valid for changes to text, not for changes to style. */
+ int line;
+ int foldLevelNow;
+ int foldLevelPrev;
+
+ DocModification(int modificationType_, int position_=0, int length_=0,
+ int linesAdded_=0, const char *text_=0, int line_=0) :
+ modificationType(modificationType_),
+ position(position_),
+ length(length_),
+ linesAdded(linesAdded_),
+ text(text_),
+ line(line_),
+ foldLevelNow(0),
+ foldLevelPrev(0) {}
+
+ DocModification(int modificationType_, const Action &act, int linesAdded_=0) :
+ modificationType(modificationType_),
+ position(act.position),
+ length(act.lenData),
+ linesAdded(linesAdded_),
+ text(act.data),
+ line(0),
+ foldLevelNow(0),
+ foldLevelPrev(0) {}
+};
+
+/**
+ * A class that wants to receive notifications from a Document must be derived from DocWatcher
+ * and implement the notification methods. It can then be added to the watcher list with AddWatcher.
+ */
+class DocWatcher {
+public:
+ virtual ~DocWatcher() {}
+
+ virtual void NotifyModifyAttempt(Document *doc, void *userData) = 0;
+ virtual void NotifySavePoint(Document *doc, void *userData, bool atSavePoint) = 0;
+ virtual void NotifyModified(Document *doc, DocModification mh, void *userData) = 0;
+ virtual void NotifyDeleted(Document *doc, void *userData) = 0;
+ virtual void NotifyStyleNeeded(Document *doc, void *userData, int endPos) = 0;
+};
+
+#endif
diff --git a/src/DocumentAccessor.cpp b/src/DocumentAccessor.cpp
new file mode 100755
index 0000000..c695c5f
--- /dev/null
+++ b/src/DocumentAccessor.cpp
@@ -0,0 +1,187 @@
+// Scintilla source code edit control
+/** @file DocumentAccessor.cxx
+ ** Rapid easy access to contents of a Scintilla.
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "SVector.h"
+#include "Accessor.h"
+#include "DocumentAccessor.h"
+#include "CellBuffer.h"
+#include "Scintilla.h"
+#include "CharClassify.h"
+#include "Document.h"
+
+DocumentAccessor::~DocumentAccessor() {
+}
+
+bool DocumentAccessor::InternalIsLeadByte(char ch) {
+ if (SC_CP_UTF8 == codePage)
+ // For lexing, all characters >= 0x80 are treated the
+ // same so none is considered a lead byte.
+ return false;
+ else
+ return Platform::IsDBCSLeadByte(codePage, ch);
+}
+
+void DocumentAccessor::Fill(int position) {
+ if (lenDoc == -1)
+ lenDoc = pdoc->Length();
+ startPos = position - slopSize;
+ if (startPos + bufferSize > lenDoc)
+ startPos = lenDoc - bufferSize;
+ if (startPos < 0)
+ startPos = 0;
+ endPos = startPos + bufferSize;
+ if (endPos > lenDoc)
+ endPos = lenDoc;
+
+ pdoc->GetCharRange(buf, startPos, endPos-startPos);
+ buf[endPos-startPos] = '\0';
+}
+
+bool DocumentAccessor::Match(int pos, const char *s) {
+ for (int i=0; *s; i++) {
+ if (*s != SafeGetCharAt(pos+i))
+ return false;
+ s++;
+ }
+ return true;
+}
+
+char DocumentAccessor::StyleAt(int position) {
+ // Mask off all bits which aren't in the 'mask'.
+ return static_cast<char>(pdoc->StyleAt(position) & mask);
+}
+
+int DocumentAccessor::GetLine(int position) {
+ return pdoc->LineFromPosition(position);
+}
+
+int DocumentAccessor::LineStart(int line) {
+ return pdoc->LineStart(line);
+}
+
+int DocumentAccessor::LevelAt(int line) {
+ return pdoc->GetLevel(line);
+}
+
+int DocumentAccessor::Length() {
+ if (lenDoc == -1)
+ lenDoc = pdoc->Length();
+ return lenDoc;
+}
+
+int DocumentAccessor::GetLineState(int line) {
+ return pdoc->GetLineState(line);
+}
+
+int DocumentAccessor::SetLineState(int line, int state) {
+ return pdoc->SetLineState(line, state);
+}
+
+void DocumentAccessor::StartAt(unsigned int start, char chMask) {
+ // Store the mask specified for use with StyleAt.
+ mask = chMask;
+ pdoc->StartStyling(start, chMask);
+ startPosStyling = start;
+}
+
+void DocumentAccessor::StartSegment(unsigned int pos) {
+ startSeg = pos;
+}
+
+void DocumentAccessor::ColourTo(unsigned int pos, int chAttr) {
+ // Only perform styling if non empty range
+ if (pos != startSeg - 1) {
+ if (pos < startSeg) {
+ Platform::DebugPrintf("Bad colour positions %d - %d\n", startSeg, pos);
+ }
+
+ if (validLen + (pos - startSeg + 1) >= bufferSize)
+ Flush();
+ if (validLen + (pos - startSeg + 1) >= bufferSize) {
+ // Too big for buffer so send directly
+ pdoc->SetStyleFor(pos - startSeg + 1, static_cast<char>(chAttr));
+ } else {
+ if (chAttr != chWhile)
+ chFlags = 0;
+ chAttr |= chFlags;
+ for (unsigned int i = startSeg; i <= pos; i++) {
+ PLATFORM_ASSERT((startPosStyling + validLen) < Length());
+ styleBuf[validLen++] = static_cast<char>(chAttr);
+ }
+ }
+ }
+ startSeg = pos+1;
+}
+
+void DocumentAccessor::SetLevel(int line, int level) {
+ pdoc->SetLevel(line, level);
+}
+
+void DocumentAccessor::Flush() {
+ startPos = extremePosition;
+ lenDoc = -1;
+ if (validLen > 0) {
+ pdoc->SetStyles(validLen, styleBuf);
+ startPosStyling += validLen;
+ validLen = 0;
+ }
+}
+
+int DocumentAccessor::IndentAmount(int line, int *flags, PFNIsCommentLeader pfnIsCommentLeader) {
+ int 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.
+
+ int pos = LineStart(line);
+ char ch = (*this)[pos];
+ int indent = 0;
+ bool inPrevPrefix = line > 0;
+ int posPrev = inPrevPrefix ? LineStart(line-1) : 0;
+ while ((ch == ' ' || ch == '\t') && (pos < end)) {
+ if (inPrevPrefix) {
+ 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 ((ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r') ||
+ (pfnIsCommentLeader && (*pfnIsCommentLeader)(*this, pos, end-pos)) )
+ return indent | SC_FOLDLEVELWHITEFLAG;
+ else
+ return indent;
+}
+
diff --git a/src/DocumentAccessor.h b/src/DocumentAccessor.h
new file mode 100755
index 0000000..7406520
--- /dev/null
+++ b/src/DocumentAccessor.h
@@ -0,0 +1,67 @@
+// Scintilla source code edit control
+/** @file DocumentAccessor.h
+ ** Implementation of BufferAccess and StylingAccess on a Scintilla
+ ** rapid easy access to contents of a Scintilla.
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+class Document;
+
+/**
+ */
+class DocumentAccessor : public Accessor {
+ // Private so DocumentAccessor objects can not be copied
+ DocumentAccessor(const DocumentAccessor &source) : Accessor(), props(source.props) {}
+ DocumentAccessor &operator=(const DocumentAccessor &) { return *this; }
+
+protected:
+ Document *pdoc;
+ PropSet &props;
+ WindowID id;
+ int lenDoc;
+
+ char styleBuf[bufferSize];
+ int validLen;
+ char chFlags;
+ char chWhile;
+ unsigned int startSeg;
+ int startPosStyling;
+ int mask;
+
+ bool InternalIsLeadByte(char ch);
+ void Fill(int position);
+
+public:
+ DocumentAccessor(Document *pdoc_, PropSet &props_, WindowID id_=0) :
+ Accessor(), pdoc(pdoc_), props(props_), id(id_),
+ lenDoc(-1), validLen(0), chFlags(0), chWhile(0),
+ startSeg(0), startPosStyling(0),
+ mask(127) { // Initialize the mask to be big enough for any lexer.
+ }
+ ~DocumentAccessor();
+ bool Match(int pos, const char *s);
+ char StyleAt(int position);
+ int GetLine(int position);
+ int LineStart(int line);
+ int LevelAt(int line);
+ int Length();
+ void Flush();
+ int GetLineState(int line);
+ int SetLineState(int line, int state);
+ int GetPropertyInt(const char *key, int defaultValue=0) {
+ return props.GetInt(key, defaultValue);
+ }
+ char *GetProperties() {
+ return props.ToString();
+ }
+ WindowID GetWindow() { return id; }
+
+ void StartAt(unsigned int start, char chMask=31);
+ void SetFlags(char chFlags_, char chWhile_) {chFlags = chFlags_; chWhile = chWhile_; };
+ unsigned int GetStartSegment() { return startSeg; }
+ void StartSegment(unsigned int pos);
+ void ColourTo(unsigned int pos, int chAttr);
+ void SetLevel(int line, int level);
+ int IndentAmount(int line, int *flags, PFNIsCommentLeader pfnIsCommentLeader = 0);
+};
diff --git a/src/Editor.cpp b/src/Editor.cpp
new file mode 100755
index 0000000..67440f1
--- /dev/null
+++ b/src/Editor.cpp
@@ -0,0 +1,7299 @@
+// Scintilla source code edit control
+/** @file Editor.cxx
+ ** Main code for the edit control.
+ **/
+// Copyright 1998-2004 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "Platform.h"
+
+#ifndef PLAT_QT
+#define INCLUDE_DEPRECATED_FEATURES
+#endif
+#include "Scintilla.h"
+
+#include "ContractionState.h"
+#include "SVector.h"
+#include "CellBuffer.h"
+#include "KeyMap.h"
+#include "Indicator.h"
+#include "XPM.h"
+#include "LineMarker.h"
+#include "Style.h"
+#include "ViewStyle.h"
+#include "CharClassify.h"
+#include "Document.h"
+#include "Editor.h"
+
+/*
+ return whether this modification represents an operation that
+ may reasonably be deferred (not done now OR [possibly] at all)
+*/
+static bool CanDeferToLastStep(const DocModification& mh) {
+ if (mh.modificationType & (SC_MOD_BEFOREINSERT|SC_MOD_BEFOREDELETE))
+ return true; // CAN skip
+ if (!(mh.modificationType & (SC_PERFORMED_UNDO|SC_PERFORMED_REDO)))
+ return false; // MUST do
+ if (mh.modificationType & SC_MULTISTEPUNDOREDO)
+ return true; // CAN skip
+ return false; // PRESUMABLY must do
+}
+
+static bool CanEliminate(const DocModification& mh) {
+ return
+ (mh.modificationType & (SC_MOD_BEFOREINSERT|SC_MOD_BEFOREDELETE)) != 0;
+}
+
+/*
+ return whether this modification represents the FINAL step
+ in a [possibly lengthy] multi-step Undo/Redo sequence
+*/
+static bool IsLastStep(const DocModification& mh) {
+ return
+ (mh.modificationType & (SC_PERFORMED_UNDO|SC_PERFORMED_REDO)) != 0
+ && (mh.modificationType & SC_MULTISTEPUNDOREDO) != 0
+ && (mh.modificationType & SC_LASTSTEPINUNDOREDO) != 0
+ && (mh.modificationType & SC_MULTILINEUNDOREDO) != 0;
+}
+
+Caret::Caret() :
+active(false), on(false), period(500) {}
+
+Timer::Timer() :
+ticking(false), ticksToWait(0), tickerID(0) {}
+
+Idler::Idler() :
+state(false), idlerID(0) {}
+
+LineLayout::LineLayout(int maxLineLength_) :
+ lineStarts(0),
+ lenLineStarts(0),
+ lineNumber(-1),
+ inCache(false),
+ maxLineLength(-1),
+ numCharsInLine(0),
+ validity(llInvalid),
+ xHighlightGuide(0),
+ highlightColumn(0),
+ selStart(0),
+ selEnd(0),
+ containsCaret(false),
+ edgeColumn(0),
+ chars(0),
+ styles(0),
+ styleBitsSet(0),
+ indicators(0),
+ positions(0),
+ hsStart(0),
+ hsEnd(0),
+ widthLine(wrapWidthInfinite),
+ lines(1) {
+ Resize(maxLineLength_);
+}
+
+LineLayout::~LineLayout() {
+ Free();
+}
+
+void LineLayout::Resize(int maxLineLength_) {
+ if (maxLineLength_ > maxLineLength) {
+ Free();
+ chars = new char[maxLineLength_ + 1];
+ styles = new unsigned char[maxLineLength_ + 1];
+ indicators = new char[maxLineLength_ + 1];
+ // Extra position allocated as sometimes the Windows
+ // GetTextExtentExPoint API writes an extra element.
+ positions = new int[maxLineLength_ + 1 + 1];
+ maxLineLength = maxLineLength_;
+ }
+}
+
+void LineLayout::Free() {
+ delete []chars;
+ chars = 0;
+ delete []styles;
+ styles = 0;
+ delete []indicators;
+ indicators = 0;
+ delete []positions;
+ positions = 0;
+ delete []lineStarts;
+ lineStarts = 0;
+}
+
+void LineLayout::Invalidate(validLevel validity_) {
+ if (validity > validity_)
+ validity = validity_;
+}
+
+void LineLayout::SetLineStart(int line, int start) {
+ if ((line >= lenLineStarts) && (line != 0)) {
+ int newMaxLines = line + 20;
+ int *newLineStarts = new int[newMaxLines];
+ if (!newLineStarts)
+ return;
+ for (int i = 0; i < newMaxLines; i++) {
+ if (i < lenLineStarts)
+ newLineStarts[i] = lineStarts[i];
+ else
+ newLineStarts[i] = 0;
+ }
+ delete []lineStarts;
+ lineStarts = newLineStarts;
+ lenLineStarts = newMaxLines;
+ }
+ lineStarts[line] = start;
+}
+
+void LineLayout::SetBracesHighlight(Range rangeLine, Position braces[],
+ char bracesMatchStyle, int xHighlight) {
+ if (rangeLine.ContainsCharacter(braces[0])) {
+ int braceOffset = braces[0] - rangeLine.start;
+ if (braceOffset < numCharsInLine) {
+ bracePreviousStyles[0] = styles[braceOffset];
+ styles[braceOffset] = bracesMatchStyle;
+ }
+ }
+ if (rangeLine.ContainsCharacter(braces[1])) {
+ int braceOffset = braces[1] - rangeLine.start;
+ if (braceOffset < numCharsInLine) {
+ bracePreviousStyles[1] = styles[braceOffset];
+ styles[braceOffset] = bracesMatchStyle;
+ }
+ }
+ if ((braces[0] >= rangeLine.start && braces[1] <= rangeLine.end) ||
+ (braces[1] >= rangeLine.start && braces[0] <= rangeLine.end)) {
+ xHighlightGuide = xHighlight;
+ }
+}
+
+void LineLayout::RestoreBracesHighlight(Range rangeLine, Position braces[]) {
+ if (rangeLine.ContainsCharacter(braces[0])) {
+ int braceOffset = braces[0] - rangeLine.start;
+ if (braceOffset < numCharsInLine) {
+ styles[braceOffset] = bracePreviousStyles[0];
+ }
+ }
+ if (rangeLine.ContainsCharacter(braces[1])) {
+ int braceOffset = braces[1] - rangeLine.start;
+ if (braceOffset < numCharsInLine) {
+ styles[braceOffset] = bracePreviousStyles[1];
+ }
+ }
+ xHighlightGuide = 0;
+}
+
+LineLayoutCache::LineLayoutCache() :
+ level(0), length(0), size(0), cache(0),
+ allInvalidated(false), styleClock(-1), useCount(0) {
+ Allocate(0);
+}
+
+LineLayoutCache::~LineLayoutCache() {
+ Deallocate();
+}
+
+void LineLayoutCache::Allocate(int length_) {
+ PLATFORM_ASSERT(cache == NULL);
+ allInvalidated = false;
+ length = length_;
+ size = length;
+ if (size > 1) {
+ size = (size / 16 + 1) * 16;
+ }
+ if (size > 0) {
+ cache = new LineLayout * [size];
+ }
+ for (int i = 0; i < size; i++)
+ cache[i] = 0;
+}
+
+void LineLayoutCache::AllocateForLevel(int linesOnScreen, int linesInDoc) {
+ PLATFORM_ASSERT(useCount == 0);
+ int lengthForLevel = 0;
+ if (level == llcCaret) {
+ lengthForLevel = 1;
+ } else if (level == llcPage) {
+ lengthForLevel = linesOnScreen + 1;
+ } else if (level == llcDocument) {
+ lengthForLevel = linesInDoc;
+ }
+ if (lengthForLevel > size) {
+ Deallocate();
+ Allocate(lengthForLevel);
+ } else {
+ if (lengthForLevel < length) {
+ for (int i = lengthForLevel; i < length; i++) {
+ delete cache[i];
+ cache[i] = 0;
+ }
+ }
+ length = lengthForLevel;
+ }
+ PLATFORM_ASSERT(length == lengthForLevel);
+ PLATFORM_ASSERT(cache != NULL || length == 0);
+}
+
+void LineLayoutCache::Deallocate() {
+ PLATFORM_ASSERT(useCount == 0);
+ for (int i = 0; i < length; i++)
+ delete cache[i];
+ delete []cache;
+ cache = 0;
+ length = 0;
+ size = 0;
+}
+
+void LineLayoutCache::Invalidate(LineLayout::validLevel validity_) {
+ if (cache && !allInvalidated) {
+ for (int i = 0; i < length; i++) {
+ if (cache[i]) {
+ cache[i]->Invalidate(validity_);
+ }
+ }
+ if (validity_ == LineLayout::llInvalid) {
+ allInvalidated = true;
+ }
+ }
+}
+
+void LineLayoutCache::SetLevel(int level_) {
+ allInvalidated = false;
+ if ((level_ != -1) && (level != level_)) {
+ level = level_;
+ Deallocate();
+ }
+}
+
+LineLayout *LineLayoutCache::Retrieve(int lineNumber, int lineCaret, int maxChars, int styleClock_,
+ int linesOnScreen, int linesInDoc) {
+ AllocateForLevel(linesOnScreen, linesInDoc);
+ if (styleClock != styleClock_) {
+ Invalidate(LineLayout::llCheckTextAndStyle);
+ styleClock = styleClock_;
+ }
+ allInvalidated = false;
+ int pos = -1;
+ LineLayout *ret = 0;
+ if (level == llcCaret) {
+ pos = 0;
+ } else if (level == llcPage) {
+ if (lineNumber == lineCaret) {
+ pos = 0;
+ } else if (length > 1) {
+ pos = 1 + (lineNumber % (length - 1));
+ }
+ } else if (level == llcDocument) {
+ pos = lineNumber;
+ }
+ if (pos >= 0) {
+ PLATFORM_ASSERT(useCount == 0);
+ if (cache && (pos < length)) {
+ if (cache[pos]) {
+ if ((cache[pos]->lineNumber != lineNumber) ||
+ (cache[pos]->maxLineLength < maxChars)) {
+ delete cache[pos];
+ cache[pos] = 0;
+ }
+ }
+ if (!cache[pos]) {
+ cache[pos] = new LineLayout(maxChars);
+ }
+ if (cache[pos]) {
+ cache[pos]->lineNumber = lineNumber;
+ cache[pos]->inCache = true;
+ ret = cache[pos];
+ useCount++;
+ }
+ }
+ }
+
+ if (!ret) {
+ ret = new LineLayout(maxChars);
+ ret->lineNumber = lineNumber;
+ }
+
+ return ret;
+}
+
+void LineLayoutCache::Dispose(LineLayout *ll) {
+ allInvalidated = false;
+ if (ll) {
+ if (!ll->inCache) {
+ delete ll;
+ } else {
+ useCount--;
+ }
+ }
+}
+
+Editor::Editor() {
+ ctrlID = 0;
+
+ stylesValid = false;
+
+ printMagnification = 0;
+ printColourMode = SC_PRINT_NORMAL;
+ printWrapState = eWrapWord;
+ cursorMode = SC_CURSORNORMAL;
+ controlCharSymbol = 0; /* Draw the control characters */
+
+ hasFocus = false;
+ hideSelection = false;
+ inOverstrike = false;
+ errorStatus = 0;
+ mouseDownCaptures = true;
+
+ bufferedDraw = true;
+ twoPhaseDraw = true;
+
+ lastClickTime = 0;
+ dwellDelay = SC_TIME_FOREVER;
+ ticksToDwell = SC_TIME_FOREVER;
+ dwelling = false;
+ ptMouseLast.x = 0;
+ ptMouseLast.y = 0;
+ inDragDrop = false;
+ dropWentOutside = false;
+ posDrag = invalidPosition;
+ posDrop = invalidPosition;
+ selectionType = selChar;
+
+ lastXChosen = 0;
+ lineAnchor = 0;
+ originalAnchorPos = 0;
+
+ selType = selStream;
+ moveExtendsSelection = false;
+ xStartSelect = 0;
+ xEndSelect = 0;
+ primarySelection = true;
+
+ caretXPolicy = CARET_SLOP | CARET_EVEN;
+ caretXSlop = 50;
+
+ caretYPolicy = CARET_EVEN;
+ caretYSlop = 0;
+
+ searchAnchor = 0;
+
+ xOffset = 0;
+ xCaretMargin = 50;
+ horizontalScrollBarVisible = true;
+ scrollWidth = 2000;
+ verticalScrollBarVisible = true;
+ endAtLastLine = true;
+ caretSticky = false;
+
+ pixmapLine = Surface::Allocate();
+ pixmapSelMargin = Surface::Allocate();
+ pixmapSelPattern = Surface::Allocate();
+ pixmapIndentGuide = Surface::Allocate();
+ pixmapIndentGuideHighlight = Surface::Allocate();
+
+ currentPos = 0;
+ anchor = 0;
+
+ targetStart = 0;
+ targetEnd = 0;
+ searchFlags = 0;
+
+ topLine = 0;
+ posTopLine = 0;
+
+ lengthForEncode = -1;
+
+ needUpdateUI = true;
+ braces[0] = invalidPosition;
+ braces[1] = invalidPosition;
+ bracesMatchStyle = STYLE_BRACEBAD;
+ highlightGuideColumn = 0;
+
+ theEdge = 0;
+
+ paintState = notPainting;
+
+ modEventMask = SC_MODEVENTMASKALL;
+
+ pdoc = new Document();
+ pdoc->AddRef();
+ pdoc->AddWatcher(this, 0);
+
+ recordingMacro = false;
+ foldFlags = 0;
+
+ wrapState = eWrapNone;
+ wrapWidth = LineLayout::wrapWidthInfinite;
+ wrapStart = wrapLineLarge;
+ wrapEnd = wrapLineLarge;
+ wrapVisualFlags = 0;
+ wrapVisualFlagsLocation = 0;
+ wrapVisualStartIndent = 0;
+ actualWrapVisualStartIndent = 0;
+
+ convertPastes = true;
+
+ hsStart = -1;
+ hsEnd = -1;
+
+ llc.SetLevel(LineLayoutCache::llcCaret);
+}
+
+Editor::~Editor() {
+ pdoc->RemoveWatcher(this, 0);
+ pdoc->Release();
+ pdoc = 0;
+ DropGraphics();
+ delete pixmapLine;
+ delete pixmapSelMargin;
+ delete pixmapSelPattern;
+ delete pixmapIndentGuide;
+ delete pixmapIndentGuideHighlight;
+}
+
+void Editor::Finalise() {
+ SetIdle(false);
+ CancelModes();
+}
+
+void Editor::DropGraphics() {
+ pixmapLine->Release();
+ pixmapSelMargin->Release();
+ pixmapSelPattern->Release();
+ pixmapIndentGuide->Release();
+ pixmapIndentGuideHighlight->Release();
+}
+
+void Editor::InvalidateStyleData() {
+ stylesValid = false;
+ palette.Release();
+ DropGraphics();
+ llc.Invalidate(LineLayout::llInvalid);
+ if (selType == selRectangle) {
+ xStartSelect = XFromPosition(anchor);
+ xEndSelect = XFromPosition(currentPos);
+ }
+}
+
+void Editor::InvalidateStyleRedraw() {
+ NeedWrapping();
+ InvalidateStyleData();
+ Redraw();
+}
+
+void Editor::RefreshColourPalette(Palette &pal, bool want) {
+ vs.RefreshColourPalette(pal, want);
+}
+
+void Editor::RefreshStyleData() {
+ if (!stylesValid) {
+ stylesValid = true;
+ AutoSurface surface(this);
+ if (surface) {
+ vs.Refresh(*surface);
+ RefreshColourPalette(palette, true);
+ palette.Allocate(wMain);
+ RefreshColourPalette(palette, false);
+ }
+ SetScrollBars();
+ }
+}
+
+PRectangle Editor::GetClientRectangle() {
+ return wMain.GetClientPosition();
+}
+
+PRectangle Editor::GetTextRectangle() {
+ PRectangle rc = GetClientRectangle();
+ rc.left += vs.fixedColumnWidth;
+ rc.right -= vs.rightMarginWidth;
+ return rc;
+}
+
+int Editor::LinesOnScreen() {
+ PRectangle rcClient = GetClientRectangle();
+ int htClient = rcClient.bottom - rcClient.top;
+ //Platform::DebugPrintf("lines on screen = %d\n", htClient / lineHeight + 1);
+ return htClient / vs.lineHeight;
+}
+
+int Editor::LinesToScroll() {
+ int retVal = LinesOnScreen() - 1;
+ if (retVal < 1)
+ return 1;
+ else
+ return retVal;
+}
+
+int Editor::MaxScrollPos() {
+ //Platform::DebugPrintf("Lines %d screen = %d maxScroll = %d\n",
+ //LinesTotal(), LinesOnScreen(), LinesTotal() - LinesOnScreen() + 1);
+ int retVal = cs.LinesDisplayed();
+ if (endAtLastLine) {
+ retVal -= LinesOnScreen();
+ } else {
+ retVal--;
+ }
+ if (retVal < 0) {
+ return 0;
+ } else {
+ return retVal;
+ }
+}
+
+static inline bool IsControlCharacter(int ch) {
+ // iscntrl returns true for lots of chars > 127 which are displayable
+ return ch >= 0 && ch < ' ';
+}
+
+const char *ControlCharacterString(unsigned char ch) {
+ const char *reps[] = {
+ "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
+ "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
+ "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
+ "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
+ };
+ if (ch < (sizeof(reps) / sizeof(reps[0]))) {
+ return reps[ch];
+ } else {
+ return "BAD";
+ }
+}
+
+/**
+ * Convenience class to ensure LineLayout objects are always disposed.
+ */
+class AutoLineLayout {
+ LineLayoutCache &llc;
+ LineLayout *ll;
+ AutoLineLayout &operator=(const AutoLineLayout &) { return * this; }
+public:
+ AutoLineLayout(LineLayoutCache &llc_, LineLayout *ll_) : llc(llc_), ll(ll_) {}
+ ~AutoLineLayout() {
+ llc.Dispose(ll);
+ ll = 0;
+ }
+ LineLayout *operator->() const {
+ return ll;
+ }
+ operator LineLayout *() const {
+ return ll;
+ }
+ void Set(LineLayout *ll_) {
+ llc.Dispose(ll);
+ ll = ll_;
+ }
+};
+
+/**
+ * Allows to iterate through the lines of a selection.
+ * Althought it can be called for a stream selection, in most cases
+ * it is inefficient and it should be used only for
+ * a rectangular or a line selection.
+ */
+class SelectionLineIterator {
+private:
+ Editor *ed;
+ int line; ///< Current line within the iteration.
+ bool forward; ///< True if iterating by increasing line number, false otherwise.
+ int selStart, selEnd; ///< Positions of the start and end of the selection relative to the start of the document.
+ int minX, maxX; ///< Left and right of selection rectangle.
+
+public:
+ int lineStart, lineEnd; ///< Line numbers, first and last lines of the selection.
+ int startPos, endPos; ///< Positions of the beginning and end of the selection on the current line.
+
+ void Reset() {
+ if (forward) {
+ line = lineStart;
+ } else {
+ line = lineEnd;
+ }
+ }
+
+ SelectionLineIterator(Editor *ed_, bool forward_ = true) : line(0), startPos(0), endPos(0) {
+ ed = ed_;
+ forward = forward_;
+ selStart = ed->SelectionStart();
+ selEnd = ed->SelectionEnd();
+ lineStart = ed->pdoc->LineFromPosition(selStart);
+ lineEnd = ed->pdoc->LineFromPosition(selEnd);
+ // Left of rectangle
+ minX = Platform::Minimum(ed->xStartSelect, ed->xEndSelect);
+ // Right of rectangle
+ maxX = Platform::Maximum(ed->xStartSelect, ed->xEndSelect);
+ Reset();
+ }
+ ~SelectionLineIterator() {}
+
+ void SetAt(int line) {
+ if (line < lineStart || line > lineEnd) {
+ startPos = endPos = INVALID_POSITION;
+ } else {
+ if (ed->selType == ed->selRectangle) {
+ // Measure line and return character closest to minX
+ startPos = ed->PositionFromLineX(line, minX);
+ // Measure line and return character closest to maxX
+ endPos = ed->PositionFromLineX(line, maxX);
+ } else if (ed->selType == ed->selLines) {
+ startPos = ed->pdoc->LineStart(line);
+ endPos = ed->pdoc->LineStart(line + 1);
+ } else { // Stream selection, here only for completion
+ if (line == lineStart) {
+ startPos = selStart;
+ } else {
+ startPos = ed->pdoc->LineStart(line);
+ }
+ if (line == lineEnd) {
+ endPos = selEnd;
+ } else {
+ endPos = ed->pdoc->LineStart(line + 1);
+ }
+ }
+ }
+ }
+ bool Iterate() {
+ SetAt(line);
+ if (forward) {
+ line++;
+ } else {
+ line--;
+ }
+ return startPos != INVALID_POSITION;
+ }
+};
+
+Point Editor::LocationFromPosition(int pos) {
+ Point pt;
+ RefreshStyleData();
+ if (pos == INVALID_POSITION)
+ return pt;
+ int line = pdoc->LineFromPosition(pos);
+ int lineVisible = cs.DisplayFromDoc(line);
+ //Platform::DebugPrintf("line=%d\n", line);
+ AutoSurface surface(this);
+ AutoLineLayout ll(llc, RetrieveLineLayout(line));
+ if (surface && ll) {
+ // -1 because of adding in for visible lines in following loop.
+ pt.y = (lineVisible - topLine - 1) * vs.lineHeight;
+ pt.x = 0;
+ unsigned int posLineStart = pdoc->LineStart(line);
+ LayoutLine(line, surface, vs, ll, wrapWidth);
+ int posInLine = pos - posLineStart;
+ // In case of very long line put x at arbitrary large position
+ if (posInLine > ll->maxLineLength) {
+ pt.x = ll->positions[ll->maxLineLength] - ll->positions[ll->LineStart(ll->lines)];
+ }
+
+ for (int subLine = 0; subLine < ll->lines; subLine++) {
+ if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
+ pt.x = ll->positions[posInLine] - ll->positions[ll->LineStart(subLine)];
+ if (actualWrapVisualStartIndent != 0) {
+ int lineStart = ll->LineStart(subLine);
+ if (lineStart != 0) // Wrapped
+ pt.x += actualWrapVisualStartIndent * vs.aveCharWidth;
+ }
+ }
+ if (posInLine >= ll->LineStart(subLine)) {
+ pt.y += vs.lineHeight;
+ }
+ }
+ pt.x += vs.fixedColumnWidth - xOffset;
+ }
+ return pt;
+}
+
+int Editor::XFromPosition(int pos) {
+ Point pt = LocationFromPosition(pos);
+ return pt.x - vs.fixedColumnWidth + xOffset;
+}
+
+int Editor::LineFromLocation(Point pt) {
+ return cs.DocFromDisplay(pt.y / vs.lineHeight + topLine);
+}
+
+void Editor::SetTopLine(int topLineNew) {
+ topLine = topLineNew;
+ posTopLine = pdoc->LineStart(cs.DocFromDisplay(topLine));
+}
+
+static inline bool IsEOLChar(char ch) {
+ return (ch == '\r') || (ch == '\n');
+}
+
+int Editor::PositionFromLocation(Point pt) {
+ RefreshStyleData();
+ pt.x = pt.x - vs.fixedColumnWidth + xOffset;
+ int visibleLine = pt.y / vs.lineHeight + topLine;
+ if (pt.y < 0) { // Division rounds towards 0
+ visibleLine = (pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine;
+ }
+ if (visibleLine < 0)
+ visibleLine = 0;
+ int lineDoc = cs.DocFromDisplay(visibleLine);
+ if (lineDoc >= pdoc->LinesTotal())
+ return pdoc->Length();
+ unsigned int posLineStart = pdoc->LineStart(lineDoc);
+ int retVal = posLineStart;
+ AutoSurface surface(this);
+ AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
+ if (surface && ll) {
+ LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
+ int lineStartSet = cs.DisplayFromDoc(lineDoc);
+ int subLine = visibleLine - lineStartSet;
+ if (subLine < ll->lines) {
+ int lineStart = ll->LineStart(subLine);
+ int lineEnd = ll->LineStart(subLine + 1);
+ int subLineStart = ll->positions[lineStart];
+
+ if (actualWrapVisualStartIndent != 0) {
+ if (lineStart != 0) // Wrapped
+ pt.x -= actualWrapVisualStartIndent * vs.aveCharWidth;
+ }
+ for (int i = lineStart; i < lineEnd; i++) {
+ if (pt.x < (((ll->positions[i] + ll->positions[i + 1]) / 2) - subLineStart) ||
+ IsEOLChar(ll->chars[i])) {
+ return pdoc->MovePositionOutsideChar(i + posLineStart, 1);
+ }
+ }
+ return lineEnd + posLineStart;
+ }
+ retVal = ll->numCharsInLine + posLineStart;
+ }
+ return retVal;
+}
+
+// Like PositionFromLocation but INVALID_POSITION returned when not near any text.
+int Editor::PositionFromLocationClose(Point pt) {
+ RefreshStyleData();
+ PRectangle rcClient = GetTextRectangle();
+ if (!rcClient.Contains(pt))
+ return INVALID_POSITION;
+ if (pt.x < vs.fixedColumnWidth)
+ return INVALID_POSITION;
+ if (pt.y < 0)
+ return INVALID_POSITION;
+ pt.x = pt.x - vs.fixedColumnWidth + xOffset;
+ int visibleLine = pt.y / vs.lineHeight + topLine;
+ if (pt.y < 0) { // Division rounds towards 0
+ visibleLine = (pt.y - (vs.lineHeight - 1)) / vs.lineHeight + topLine;
+ }
+ int lineDoc = cs.DocFromDisplay(visibleLine);
+ if (lineDoc < 0)
+ return INVALID_POSITION;
+ if (lineDoc >= pdoc->LinesTotal())
+ return INVALID_POSITION;
+ AutoSurface surface(this);
+ AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
+ if (surface && ll) {
+ LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
+ unsigned int posLineStart = pdoc->LineStart(lineDoc);
+ int lineStartSet = cs.DisplayFromDoc(lineDoc);
+ int subLine = visibleLine - lineStartSet;
+ if (subLine < ll->lines) {
+ int lineStart = ll->LineStart(subLine);
+ int lineEnd = ll->LineStart(subLine + 1);
+ int subLineStart = ll->positions[lineStart];
+
+ if (actualWrapVisualStartIndent != 0) {
+ if (lineStart != 0) // Wrapped
+ pt.x -= actualWrapVisualStartIndent * vs.aveCharWidth;
+ }
+ for (int i = lineStart; i < lineEnd; i++) {
+ if (pt.x < (((ll->positions[i] + ll->positions[i + 1]) / 2) - subLineStart) ||
+ IsEOLChar(ll->chars[i])) {
+ return pdoc->MovePositionOutsideChar(i + posLineStart, 1);
+ }
+ }
+ if (pt.x < (ll->positions[lineEnd] - subLineStart)) {
+ return pdoc->MovePositionOutsideChar(lineEnd + posLineStart, 1);
+ }
+ }
+ }
+
+ return INVALID_POSITION;
+}
+
+/**
+ * Find the document position corresponding to an x coordinate on a particular document line.
+ * Ensure is between whole characters when document is in multi-byte or UTF-8 mode.
+ */
+int Editor::PositionFromLineX(int lineDoc, int x) {
+ RefreshStyleData();
+ if (lineDoc >= pdoc->LinesTotal())
+ return pdoc->Length();
+ //Platform::DebugPrintf("Position of (%d,%d) line = %d top=%d\n", pt.x, pt.y, line, topLine);
+ AutoSurface surface(this);
+ AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
+ int retVal = 0;
+ if (surface && ll) {
+ unsigned int posLineStart = pdoc->LineStart(lineDoc);
+ LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
+ retVal = ll->numCharsInLine + posLineStart;
+ int subLine = 0;
+ int lineStart = ll->LineStart(subLine);
+ int lineEnd = ll->LineStart(subLine + 1);
+ int subLineStart = ll->positions[lineStart];
+
+ if (actualWrapVisualStartIndent != 0) {
+ if (lineStart != 0) // Wrapped
+ x -= actualWrapVisualStartIndent * vs.aveCharWidth;
+ }
+ for (int i = lineStart; i < lineEnd; i++) {
+ if (x < (((ll->positions[i] + ll->positions[i + 1]) / 2) - subLineStart) ||
+ IsEOLChar(ll->chars[i])) {
+ retVal = pdoc->MovePositionOutsideChar(i + posLineStart, 1);
+ break;
+ }
+ }
+ }
+ return retVal;
+}
+
+/**
+ * If painting then abandon the painting because a wider redraw is needed.
+ * @return true if calling code should stop drawing.
+ */
+bool Editor::AbandonPaint() {
+ if ((paintState == painting) && !paintingAllText) {
+ paintState = paintAbandoned;
+ }
+ return paintState == paintAbandoned;
+}
+
+void Editor::RedrawRect(PRectangle rc) {
+ //Platform::DebugPrintf("Redraw %0d,%0d - %0d,%0d\n", rc.left, rc.top, rc.right, rc.bottom);
+
+ // Clip the redraw rectangle into the client area
+ PRectangle rcClient = GetClientRectangle();
+ if (rc.top < rcClient.top)
+ rc.top = rcClient.top;
+ if (rc.bottom > rcClient.bottom)
+ rc.bottom = rcClient.bottom;
+ if (rc.left < rcClient.left)
+ rc.left = rcClient.left;
+ if (rc.right > rcClient.right)
+ rc.right = rcClient.right;
+
+ if ((rc.bottom > rc.top) && (rc.right > rc.left)) {
+ wMain.InvalidateRectangle(rc);
+ }
+}
+
+void Editor::Redraw() {
+ //Platform::DebugPrintf("Redraw all\n");
+ PRectangle rcClient = GetClientRectangle();
+ wMain.InvalidateRectangle(rcClient);
+ //wMain.InvalidateAll();
+}
+
+void Editor::RedrawSelMargin(int line) {
+ if (!AbandonPaint()) {
+ if (vs.maskInLine) {
+ Redraw();
+ } else {
+ PRectangle rcSelMargin = GetClientRectangle();
+ rcSelMargin.right = vs.fixedColumnWidth;
+ if (line != -1) {
+ int position = pdoc->LineStart(line);
+ PRectangle rcLine = RectangleFromRange(position, position);
+ rcSelMargin.top = rcLine.top;
+ rcSelMargin.bottom = rcLine.bottom;
+ }
+ wMain.InvalidateRectangle(rcSelMargin);
+ }
+ }
+}
+
+PRectangle Editor::RectangleFromRange(int start, int end) {
+ int minPos = start;
+ if (minPos > end)
+ minPos = end;
+ int maxPos = start;
+ if (maxPos < end)
+ maxPos = end;
+ int minLine = cs.DisplayFromDoc(pdoc->LineFromPosition(minPos));
+ int lineDocMax = pdoc->LineFromPosition(maxPos);
+ int maxLine = cs.DisplayFromDoc(lineDocMax) + cs.GetHeight(lineDocMax) - 1;
+ PRectangle rcClient = GetTextRectangle();
+ PRectangle rc;
+ rc.left = vs.fixedColumnWidth;
+ rc.top = (minLine - topLine) * vs.lineHeight;
+ if (rc.top < 0)
+ rc.top = 0;
+ rc.right = rcClient.right;
+ rc.bottom = (maxLine - topLine + 1) * vs.lineHeight;
+ // Ensure PRectangle is within 16 bit space
+ rc.top = Platform::Clamp(rc.top, -32000, 32000);
+ rc.bottom = Platform::Clamp(rc.bottom, -32000, 32000);
+
+ return rc;
+}
+
+void Editor::InvalidateRange(int start, int end) {
+ RedrawRect(RectangleFromRange(start, end));
+}
+
+int Editor::CurrentPosition() {
+ return currentPos;
+}
+
+bool Editor::SelectionEmpty() {
+ return anchor == currentPos;
+}
+
+int Editor::SelectionStart() {
+ return Platform::Minimum(currentPos, anchor);
+}
+
+int Editor::SelectionEnd() {
+ return Platform::Maximum(currentPos, anchor);
+}
+
+void Editor::SetRectangularRange() {
+ if (selType == selRectangle) {
+ xStartSelect = XFromPosition(anchor);
+ xEndSelect = XFromPosition(currentPos);
+ }
+}
+
+void Editor::InvalidateSelection(int currentPos_, int anchor_) {
+ int firstAffected = anchor;
+ if (firstAffected > currentPos)
+ firstAffected = currentPos;
+ if (firstAffected > anchor_)
+ firstAffected = anchor_;
+ if (firstAffected > currentPos_)
+ firstAffected = currentPos_;
+ int lastAffected = anchor;
+ if (lastAffected < currentPos)
+ lastAffected = currentPos;
+ if (lastAffected < anchor_)
+ lastAffected = anchor_;
+ if (lastAffected < (currentPos_ + 1)) // +1 ensures caret repainted
+ lastAffected = (currentPos_ + 1);
+ needUpdateUI = true;
+ InvalidateRange(firstAffected, lastAffected);
+}
+
+void Editor::SetSelection(int currentPos_, int anchor_) {
+ currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_);
+ anchor_ = pdoc->ClampPositionIntoDocument(anchor_);
+ if ((currentPos != currentPos_) || (anchor != anchor_)) {
+ InvalidateSelection(currentPos_, anchor_);
+ currentPos = currentPos_;
+ anchor = anchor_;
+ }
+ SetRectangularRange();
+ ClaimSelection();
+}
+
+void Editor::SetSelection(int currentPos_) {
+ currentPos_ = pdoc->ClampPositionIntoDocument(currentPos_);
+ if (currentPos != currentPos_) {
+ InvalidateSelection(currentPos_, currentPos_);
+ currentPos = currentPos_;
+ }
+ SetRectangularRange();
+ ClaimSelection();
+}
+
+void Editor::SetEmptySelection(int currentPos_) {
+ selType = selStream;
+ moveExtendsSelection = false;
+ SetSelection(currentPos_, currentPos_);
+}
+
+bool Editor::RangeContainsProtected(int start, int end) const {
+ if (vs.ProtectionActive()) {
+ if (start > end) {
+ int t = start;
+ start = end;
+ end = t;
+ }
+ int mask = pdoc->stylingBitsMask;
+ for (int pos = start; pos < end; pos++) {
+ if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected())
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Editor::SelectionContainsProtected() {
+ // DONE, but untested...: make support rectangular selection
+ bool scp = false;
+ if (selType == selStream) {
+ scp = RangeContainsProtected(anchor, currentPos);
+ } else {
+ SelectionLineIterator lineIterator(this);
+ while (lineIterator.Iterate()) {
+ if (RangeContainsProtected(lineIterator.startPos, lineIterator.endPos)) {
+ scp = true;
+ break;
+ }
+ }
+ }
+ return scp;
+}
+
+/**
+ * Asks document to find a good position and then moves out of any invisible positions.
+ */
+int Editor::MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd) {
+ pos = pdoc->MovePositionOutsideChar(pos, moveDir, checkLineEnd);
+ if (vs.ProtectionActive()) {
+ int mask = pdoc->stylingBitsMask;
+ if (moveDir > 0) {
+ if ((pos > 0) && vs.styles[pdoc->StyleAt(pos - 1) & mask].IsProtected()) {
+ while ((pos < pdoc->Length()) &&
+ (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected()))
+ pos++;
+ }
+ } else if (moveDir < 0) {
+ if (vs.styles[pdoc->StyleAt(pos) & mask].IsProtected()) {
+ while ((pos > 0) &&
+ (vs.styles[pdoc->StyleAt(pos - 1) & mask].IsProtected()))
+ pos--;
+ }
+ }
+ }
+ return pos;
+}
+
+int Editor::MovePositionTo(int newPos, selTypes sel, bool ensureVisible) {
+ int delta = newPos - currentPos;
+ newPos = pdoc->ClampPositionIntoDocument(newPos);
+ newPos = MovePositionOutsideChar(newPos, delta);
+ if (sel != noSel) {
+ selType = sel;
+ }
+ if (sel != noSel || moveExtendsSelection) {
+ SetSelection(newPos);
+ } else {
+ SetEmptySelection(newPos);
+ }
+ ShowCaretAtCurrentPosition();
+ if (ensureVisible) {
+ EnsureCaretVisible();
+ }
+ NotifyMove(newPos);
+ return 0;
+}
+
+int Editor::MovePositionSoVisible(int pos, int moveDir) {
+ pos = pdoc->ClampPositionIntoDocument(pos);
+ pos = MovePositionOutsideChar(pos, moveDir);
+ int lineDoc = pdoc->LineFromPosition(pos);
+ if (cs.GetVisible(lineDoc)) {
+ return pos;
+ } else {
+ int lineDisplay = cs.DisplayFromDoc(lineDoc);
+ if (moveDir > 0) {
+ // lineDisplay is already line before fold as lines in fold use display line of line after fold
+ lineDisplay = Platform::Clamp(lineDisplay, 0, cs.LinesDisplayed());
+ return pdoc->LineStart(cs.DocFromDisplay(lineDisplay));
+ } else {
+ lineDisplay = Platform::Clamp(lineDisplay - 1, 0, cs.LinesDisplayed());
+ return pdoc->LineEnd(cs.DocFromDisplay(lineDisplay));
+ }
+ }
+}
+
+/**
+ * Choose the x position that the caret will try to stick to
+ * as it moves up and down.
+ */
+void Editor::SetLastXChosen() {
+ Point pt = LocationFromPosition(currentPos);
+ lastXChosen = pt.x;
+}
+
+void Editor::ScrollTo(int line, bool moveThumb) {
+ int topLineNew = Platform::Clamp(line, 0, MaxScrollPos());
+ if (topLineNew != topLine) {
+ // Try to optimise small scrolls
+ int linesToMove = topLine - topLineNew;
+ SetTopLine(topLineNew);
+ ShowCaretAtCurrentPosition();
+ // Perform redraw rather than scroll if many lines would be redrawn anyway.
+#ifndef UNDER_CE
+ if (abs(linesToMove) <= 10) {
+ ScrollText(linesToMove);
+ } else {
+ Redraw();
+ }
+#else
+ Redraw();
+#endif
+ if (moveThumb) {
+ SetVerticalScrollPos();
+ }
+ }
+}
+
+void Editor::ScrollText(int /* linesToMove */) {
+ //Platform::DebugPrintf("Editor::ScrollText %d\n", linesToMove);
+ Redraw();
+}
+
+void Editor::HorizontalScrollTo(int xPos) {
+ //Platform::DebugPrintf("HorizontalScroll %d\n", xPos);
+ if (xPos < 0)
+ xPos = 0;
+ if ((wrapState == eWrapNone) && (xOffset != xPos)) {
+ xOffset = xPos;
+ SetHorizontalScrollPos();
+ RedrawRect(GetClientRectangle());
+ }
+}
+
+void Editor::MoveCaretInsideView(bool ensureVisible) {
+ PRectangle rcClient = GetTextRectangle();
+ Point pt = LocationFromPosition(currentPos);
+ if (pt.y < rcClient.top) {
+ MovePositionTo(PositionFromLocation(
+ Point(lastXChosen, rcClient.top)),
+ noSel, ensureVisible);
+ } else if ((pt.y + vs.lineHeight - 1) > rcClient.bottom) {
+ int yOfLastLineFullyDisplayed = rcClient.top + (LinesOnScreen() - 1) * vs.lineHeight;
+ MovePositionTo(PositionFromLocation(
+ Point(lastXChosen, rcClient.top + yOfLastLineFullyDisplayed)),
+ noSel, ensureVisible);
+ }
+}
+
+int Editor::DisplayFromPosition(int pos) {
+ int lineDoc = pdoc->LineFromPosition(pos);
+ int lineDisplay = cs.DisplayFromDoc(lineDoc);
+ AutoSurface surface(this);
+ AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc));
+ if (surface && ll) {
+ LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
+ unsigned int posLineStart = pdoc->LineStart(lineDoc);
+ int posInLine = pos - posLineStart;
+ lineDisplay--; // To make up for first increment ahead.
+ for (int subLine = 0; subLine < ll->lines; subLine++) {
+ if (posInLine >= ll->LineStart(subLine)) {
+ lineDisplay++;
+ }
+ }
+ }
+ return lineDisplay;
+}
+
+/**
+ * Ensure the caret is reasonably visible in context.
+ *
+Caret policy in SciTE
+
+If slop is set, we can define a slop value.
+This value defines an unwanted zone (UZ) where the caret is... unwanted.
+This zone is defined as a number of pixels near the vertical margins,
+and as a number of lines near the horizontal margins.
+By keeping the caret away from the edges, it is seen within its context,
+so it is likely that the identifier that the caret is on can be completely seen,
+and that the current line is seen with some of the lines following it which are
+often dependent on that line.
+
+If strict is set, the policy is enforced... strictly.
+The caret is centred on the display if slop is not set,
+and cannot go in the UZ if slop is set.
+
+If jumps is set, the display is moved more energetically
+so the caret can move in the same direction longer before the policy is applied again.
+'3UZ' notation is used to indicate three time the size of the UZ as a distance to the margin.
+
+If even is not set, instead of having symmetrical UZs,
+the left and bottom UZs are extended up to right and top UZs respectively.
+This way, we favour the displaying of useful information: the begining of lines,
+where most code reside, and the lines after the caret, eg. the body of a function.
+
+ | | | | |
+slop | strict | jumps | even | Caret can go to the margin | When reaching limitÝ(caret going out of
+ | | | | | visibility or going into the UZ) display is...
+-----+--------+-------+------+--------------------------------------------+--------------------------------------------------------------
+ 0 | 0 | 0 | 0 | Yes | moved to put caret on top/on right
+ 0 | 0 | 0 | 1 | Yes | moved by one position
+ 0 | 0 | 1 | 0 | Yes | moved to put caret on top/on right
+ 0 | 0 | 1 | 1 | Yes | centred on the caret
+ 0 | 1 | - | 0 | Caret is always on top/on right of display | -
+ 0 | 1 | - | 1 | No, caret is always centred | -
+ 1 | 0 | 0 | 0 | Yes | moved to put caret out of the asymmetrical UZ
+ 1 | 0 | 0 | 1 | Yes | moved to put caret out of the UZ
+ 1 | 0 | 1 | 0 | Yes | moved to put caret at 3UZ of the top or right margin
+ 1 | 0 | 1 | 1 | Yes | moved to put caret at 3UZ of the margin
+ 1 | 1 | - | 0 | Caret is always at UZ of top/right margin | -
+ 1 | 1 | 0 | 1 | No, kept out of UZ | moved by one position
+ 1 | 1 | 1 | 1 | No, kept out of UZ | moved to put caret at 3UZ of the margin
+*/
+void Editor::EnsureCaretVisible(bool useMargin, bool vert, bool horiz) {
+ //Platform::DebugPrintf("EnsureCaretVisible %d %s\n", xOffset, useMargin ? " margin" : " ");
+ PRectangle rcClient = GetTextRectangle();
+ //int rcClientFullWidth = rcClient.Width();
+ int posCaret = currentPos;
+ if (posDrag >= 0) {
+ posCaret = posDrag;
+ }
+ Point pt = LocationFromPosition(posCaret);
+ Point ptBottomCaret = pt;
+ ptBottomCaret.y += vs.lineHeight - 1;
+ int lineCaret = DisplayFromPosition(posCaret);
+ bool bSlop, bStrict, bJump, bEven;
+
+ // Vertical positioning
+ if (vert && (pt.y < rcClient.top || ptBottomCaret.y > rcClient.bottom || (caretYPolicy & CARET_STRICT) != 0)) {
+ int linesOnScreen = LinesOnScreen();
+ int halfScreen = Platform::Maximum(linesOnScreen - 1, 2) / 2;
+ int newTopLine = topLine;
+ bSlop = (caretYPolicy & CARET_SLOP) != 0;
+ bStrict = (caretYPolicy & CARET_STRICT) != 0;
+ bJump = (caretYPolicy & CARET_JUMPS) != 0;
+ bEven = (caretYPolicy & CARET_EVEN) != 0;
+
+ // It should be possible to scroll the window to show the caret,
+ // but this fails to remove the caret on GTK+
+ if (bSlop) { // A margin is defined
+ int yMoveT, yMoveB;
+ if (bStrict) {
+ int yMarginT, yMarginB;
+ if (!useMargin) {
+ // In drag mode, avoid moves
+ // otherwise, a double click will select several lines.
+ yMarginT = yMarginB = 0;
+ } else {
+ // yMarginT must equal to caretYSlop, with a minimum of 1 and
+ // a maximum of slightly less than half the heigth of the text area.
+ yMarginT = Platform::Clamp(caretYSlop, 1, halfScreen);
+ if (bEven) {
+ yMarginB = yMarginT;
+ } else {
+ yMarginB = linesOnScreen - yMarginT - 1;
+ }
+ }
+ yMoveT = yMarginT;
+ if (bEven) {
+ if (bJump) {
+ yMoveT = Platform::Clamp(caretYSlop * 3, 1, halfScreen);
+ }
+ yMoveB = yMoveT;
+ } else {
+ yMoveB = linesOnScreen - yMoveT - 1;
+ }
+ if (lineCaret < topLine + yMarginT) {
+ // Caret goes too high
+ newTopLine = lineCaret - yMoveT;
+ } else if (lineCaret > topLine + linesOnScreen - 1 - yMarginB) {
+ // Caret goes too low
+ newTopLine = lineCaret - linesOnScreen + 1 + yMoveB;
+ }
+ } else { // Not strict
+ yMoveT = bJump ? caretYSlop * 3 : caretYSlop;
+ yMoveT = Platform::Clamp(yMoveT, 1, halfScreen);
+ if (bEven) {
+ yMoveB = yMoveT;
+ } else {
+ yMoveB = linesOnScreen - yMoveT - 1;
+ }
+ if (lineCaret < topLine) {
+ // Caret goes too high
+ newTopLine = lineCaret - yMoveT;
+ } else if (lineCaret > topLine + linesOnScreen - 1) {
+ // Caret goes too low
+ newTopLine = lineCaret - linesOnScreen + 1 + yMoveB;
+ }
+ }
+ } else { // No slop
+ if (!bStrict && !bJump) {
+ // Minimal move
+ if (lineCaret < topLine) {
+ // Caret goes too high
+ newTopLine = lineCaret;
+ } else if (lineCaret > topLine + linesOnScreen - 1) {
+ // Caret goes too low
+ if (bEven) {
+ newTopLine = lineCaret - linesOnScreen + 1;
+ } else {
+ newTopLine = lineCaret;
+ }
+ }
+ } else { // Strict or going out of display
+ if (bEven) {
+ // Always center caret
+ newTopLine = lineCaret - halfScreen;
+ } else {
+ // Always put caret on top of display
+ newTopLine = lineCaret;
+ }
+ }
+ }
+ newTopLine = Platform::Clamp(newTopLine, 0, MaxScrollPos());
+ if (newTopLine != topLine) {
+ Redraw();
+ SetTopLine(newTopLine);
+ SetVerticalScrollPos();
+ }
+ }
+
+ // Horizontal positioning
+ if (horiz && (wrapState == eWrapNone)) {
+ int halfScreen = Platform::Maximum(rcClient.Width() - 4, 4) / 2;
+ int xOffsetNew = xOffset;
+ bSlop = (caretXPolicy & CARET_SLOP) != 0;
+ bStrict = (caretXPolicy & CARET_STRICT) != 0;
+ bJump = (caretXPolicy & CARET_JUMPS) != 0;
+ bEven = (caretXPolicy & CARET_EVEN) != 0;
+
+ if (bSlop) { // A margin is defined
+ int xMoveL, xMoveR;
+ if (bStrict) {
+ int xMarginL, xMarginR;
+ if (!useMargin) {
+ // In drag mode, avoid moves unless very near of the margin
+ // otherwise, a simple click will select text.
+ xMarginL = xMarginR = 2;
+ } else {
+ // xMargin must equal to caretXSlop, with a minimum of 2 and
+ // a maximum of slightly less than half the width of the text area.
+ xMarginR = Platform::Clamp(caretXSlop, 2, halfScreen);
+ if (bEven) {
+ xMarginL = xMarginR;
+ } else {
+ xMarginL = rcClient.Width() - xMarginR - 4;
+ }
+ }
+ if (bJump && bEven) {
+ // Jump is used only in even mode
+ xMoveL = xMoveR = Platform::Clamp(caretXSlop * 3, 1, halfScreen);
+ } else {
+ xMoveL = xMoveR = 0; // Not used, avoid a warning
+ }
+ if (pt.x < rcClient.left + xMarginL) {
+ // Caret is on the left of the display
+ if (bJump && bEven) {
+ xOffsetNew -= xMoveL;
+ } else {
+ // Move just enough to allow to display the caret
+ xOffsetNew -= (rcClient.left + xMarginL) - pt.x;
+ }
+ } else if (pt.x >= rcClient.right - xMarginR) {
+ // Caret is on the right of the display
+ if (bJump && bEven) {
+ xOffsetNew += xMoveR;
+ } else {
+ // Move just enough to allow to display the caret
+ xOffsetNew += pt.x - (rcClient.right - xMarginR) + 1;
+ }
+ }
+ } else { // Not strict
+ xMoveR = bJump ? caretXSlop * 3 : caretXSlop;
+ xMoveR = Platform::Clamp(xMoveR, 1, halfScreen);
+ if (bEven) {
+ xMoveL = xMoveR;
+ } else {
+ xMoveL = rcClient.Width() - xMoveR - 4;
+ }
+ if (pt.x < rcClient.left) {
+ // Caret is on the left of the display
+ xOffsetNew -= xMoveL;
+ } else if (pt.x >= rcClient.right) {
+ // Caret is on the right of the display
+ xOffsetNew += xMoveR;
+ }
+ }
+ } else { // No slop
+ if (bStrict ||
+ (bJump && (pt.x < rcClient.left || pt.x >= rcClient.right))) {
+ // Strict or going out of display
+ if (bEven) {
+ // Center caret
+ xOffsetNew += pt.x - rcClient.left - halfScreen;
+ } else {
+ // Put caret on right
+ xOffsetNew += pt.x - rcClient.right + 1;
+ }
+ } else {
+ // Move just enough to allow to display the caret
+ if (pt.x < rcClient.left) {
+ // Caret is on the left of the display
+ if (bEven) {
+ xOffsetNew -= rcClient.left - pt.x;
+ } else {
+ xOffsetNew += pt.x - rcClient.right + 1;
+ }
+ } else if (pt.x >= rcClient.right) {
+ // Caret is on the right of the display
+ xOffsetNew += pt.x - rcClient.right + 1;
+ }
+ }
+ }
+ // In case of a jump (find result) largely out of display, adjust the offset to display the caret
+ if (pt.x + xOffset < rcClient.left + xOffsetNew) {
+ xOffsetNew = pt.x + xOffset - rcClient.left;
+ } else if (pt.x + xOffset >= rcClient.right + xOffsetNew) {
+ xOffsetNew = pt.x + xOffset - rcClient.right + 1;
+ }
+ if (xOffsetNew < 0) {
+ xOffsetNew = 0;
+ }
+ if (xOffset != xOffsetNew) {
+ xOffset = xOffsetNew;
+ if (xOffsetNew > 0) {
+ PRectangle rcText = GetTextRectangle();
+ if (horizontalScrollBarVisible == true &&
+ rcText.Width() + xOffset > scrollWidth) {
+ scrollWidth = xOffset + rcText.Width();
+ SetScrollBars();
+ }
+ }
+ SetHorizontalScrollPos();
+ Redraw();
+ }
+ }
+ UpdateSystemCaret();
+}
+
+void Editor::ShowCaretAtCurrentPosition() {
+ if (hasFocus) {
+ caret.active = true;
+ caret.on = true;
+ SetTicking(true);
+ } else {
+ caret.active = false;
+ caret.on = false;
+ }
+ InvalidateCaret();
+}
+
+void Editor::DropCaret() {
+ caret.active = false;
+ InvalidateCaret();
+}
+
+void Editor::InvalidateCaret() {
+ if (posDrag >= 0)
+ InvalidateRange(posDrag, posDrag + 1);
+ else
+ InvalidateRange(currentPos, currentPos + 1);
+ UpdateSystemCaret();
+}
+
+void Editor::UpdateSystemCaret() {
+}
+
+void Editor::NeedWrapping(int docLineStart, int docLineEnd) {
+ docLineStart = Platform::Clamp(docLineStart, 0, pdoc->LinesTotal());
+ if (wrapStart > docLineStart) {
+ wrapStart = docLineStart;
+ llc.Invalidate(LineLayout::llPositions);
+ }
+ if (wrapEnd < docLineEnd) {
+ wrapEnd = docLineEnd;
+ }
+ wrapEnd = Platform::Clamp(wrapEnd, 0, pdoc->LinesTotal());
+ // Wrap lines during idle.
+ if ((wrapState != eWrapNone) && (wrapEnd != wrapStart)) {
+ SetIdle(true);
+ }
+}
+
+// Check if wrapping needed and perform any needed wrapping.
+// fullwrap: if true, all lines which need wrapping will be done,
+// in this single call.
+// priorityWrapLineStart: If greater than zero, all lines starting from
+// here to 1 page + 100 lines past will be wrapped (even if there are
+// more lines under wrapping process in idle).
+// If it is neither fullwrap, nor priorityWrap, then 1 page + 100 lines will be
+// wrapped, if there are any wrapping going on in idle. (Generally this
+// condition is called only from idler).
+// Return true if wrapping occurred.
+bool Editor::WrapLines(bool fullWrap, int priorityWrapLineStart) {
+ // If there are any pending wraps, do them during idle if possible.
+ int linesInOneCall = LinesOnScreen() + 100;
+ if (wrapState != eWrapNone) {
+ if (wrapStart < wrapEnd) {
+ if (!SetIdle(true)) {
+ // Idle processing not supported so full wrap required.
+ fullWrap = true;
+ }
+ }
+ if (!fullWrap && priorityWrapLineStart >= 0 &&
+ // .. and if the paint window is outside pending wraps
+ (((priorityWrapLineStart + linesInOneCall) < wrapStart) ||
+ (priorityWrapLineStart > wrapEnd))) {
+ // No priority wrap pending
+ return false;
+ }
+ }
+ int goodTopLine = topLine;
+ bool wrapOccurred = false;
+ if (wrapStart <= pdoc->LinesTotal()) {
+ if (wrapState == eWrapNone) {
+ if (wrapWidth != LineLayout::wrapWidthInfinite) {
+ wrapWidth = LineLayout::wrapWidthInfinite;
+ for (int lineDoc = 0; lineDoc < pdoc->LinesTotal(); lineDoc++) {
+ cs.SetHeight(lineDoc, 1);
+ }
+ wrapOccurred = true;
+ }
+ wrapStart = wrapLineLarge;
+ wrapEnd = wrapLineLarge;
+ } else {
+ if (wrapEnd >= pdoc->LinesTotal())
+ wrapEnd = pdoc->LinesTotal();
+ //ElapsedTime et;
+ int lineDocTop = cs.DocFromDisplay(topLine);
+ int subLineTop = topLine - cs.DisplayFromDoc(lineDocTop);
+ PRectangle rcTextArea = GetClientRectangle();
+ rcTextArea.left = vs.fixedColumnWidth;
+ rcTextArea.right -= vs.rightMarginWidth;
+ wrapWidth = rcTextArea.Width();
+ // Ensure all of the document is styled.
+ pdoc->EnsureStyledTo(pdoc->Length());
+ RefreshStyleData();
+ AutoSurface surface(this);
+ if (surface) {
+ bool priorityWrap = false;
+ int lastLineToWrap = wrapEnd;
+ int lineToWrap = wrapStart;
+ if (!fullWrap) {
+ if (priorityWrapLineStart >= 0) {
+ // This is a priority wrap.
+ lineToWrap = priorityWrapLineStart;
+ lastLineToWrap = priorityWrapLineStart + linesInOneCall;
+ priorityWrap = true;
+ } else {
+ // This is idle wrap.
+ lastLineToWrap = wrapStart + linesInOneCall;
+ }
+ if (lastLineToWrap >= wrapEnd)
+ lastLineToWrap = wrapEnd;
+ } // else do a fullWrap.
+
+ // Platform::DebugPrintf("Wraplines: full = %d, priorityStart = %d (wrapping: %d to %d)\n", fullWrap, priorityWrapLineStart, lineToWrap, lastLineToWrap);
+ // Platform::DebugPrintf("Pending wraps: %d to %d\n", wrapStart, wrapEnd);
+ while (lineToWrap < lastLineToWrap) {
+ AutoLineLayout ll(llc, RetrieveLineLayout(lineToWrap));
+ int linesWrapped = 1;
+ if (ll) {
+ LayoutLine(lineToWrap, surface, vs, ll, wrapWidth);
+ linesWrapped = ll->lines;
+ }
+ if (cs.SetHeight(lineToWrap, linesWrapped)) {
+ wrapOccurred = true;
+ }
+ lineToWrap++;
+ }
+ if (!priorityWrap)
+ wrapStart = lineToWrap;
+ // If wrapping is done, bring it to resting position
+ if (wrapStart >= wrapEnd) {
+ wrapStart = wrapLineLarge;
+ wrapEnd = wrapLineLarge;
+ }
+ }
+ goodTopLine = cs.DisplayFromDoc(lineDocTop);
+ if (subLineTop < cs.GetHeight(lineDocTop))
+ goodTopLine += subLineTop;
+ else
+ goodTopLine += cs.GetHeight(lineDocTop);
+ //double durWrap = et.Duration(true);
+ //Platform::DebugPrintf("Wrap:%9.6g \n", durWrap);
+ }
+ }
+ if (wrapOccurred) {
+ SetScrollBars();
+ SetTopLine(Platform::Clamp(goodTopLine, 0, MaxScrollPos()));
+ SetVerticalScrollPos();
+ }
+ return wrapOccurred;
+}
+
+void Editor::LinesJoin() {
+ if (!RangeContainsProtected(targetStart, targetEnd)) {
+ pdoc->BeginUndoAction();
+ bool prevNonWS = true;
+ for (int pos = targetStart; pos < targetEnd; pos++) {
+ if (IsEOLChar(pdoc->CharAt(pos))) {
+ targetEnd -= pdoc->LenChar(pos);
+ pdoc->DelChar(pos);
+ if (prevNonWS) {
+ // Ensure at least one space separating previous lines
+ pdoc->InsertChar(pos, ' ');
+ }
+ } else {
+ prevNonWS = pdoc->CharAt(pos) != ' ';
+ }
+ }
+ pdoc->EndUndoAction();
+ }
+}
+
+const char *StringFromEOLMode(int eolMode) {
+ if (eolMode == SC_EOL_CRLF) {
+ return "\r\n";
+ } else if (eolMode == SC_EOL_CR) {
+ return "\r";
+ } else {
+ return "\n";
+ }
+}
+
+void Editor::LinesSplit(int pixelWidth) {
+ if (!RangeContainsProtected(targetStart, targetEnd)) {
+ if (pixelWidth == 0) {
+ PRectangle rcText = GetTextRectangle();
+ pixelWidth = rcText.Width();
+ }
+ int lineStart = pdoc->LineFromPosition(targetStart);
+ int lineEnd = pdoc->LineFromPosition(targetEnd);
+ const char *eol = StringFromEOLMode(pdoc->eolMode);
+ pdoc->BeginUndoAction();
+ for (int line = lineStart; line <= lineEnd; line++) {
+ AutoSurface surface(this);
+ AutoLineLayout ll(llc, RetrieveLineLayout(line));
+ if (surface && ll) {
+ unsigned int posLineStart = pdoc->LineStart(line);
+ LayoutLine(line, surface, vs, ll, pixelWidth);
+ for (int subLine = 1; subLine < ll->lines; subLine++) {
+ pdoc->InsertString(posLineStart + (subLine - 1) * strlen(eol) +
+ ll->LineStart(subLine), eol);
+ targetEnd += static_cast<int>(strlen(eol));
+ }
+ }
+ lineEnd = pdoc->LineFromPosition(targetEnd);
+ }
+ pdoc->EndUndoAction();
+ }
+}
+
+int Editor::SubstituteMarkerIfEmpty(int markerCheck, int markerDefault) {
+ if (vs.markers[markerCheck].markType == SC_MARK_EMPTY)
+ return markerDefault;
+ return markerCheck;
+}
+
+// Avoid 64 bit compiler warnings.
+// Scintilla does not support text buffers larger than 2**31
+static int istrlen(const char *s) {
+ return static_cast<int>(strlen(s));
+}
+
+void Editor::PaintSelMargin(Surface *surfWindow, PRectangle &rc) {
+ if (vs.fixedColumnWidth == 0)
+ return;
+
+ PRectangle rcMargin = GetClientRectangle();
+ rcMargin.right = vs.fixedColumnWidth;
+
+ if (!rc.Intersects(rcMargin))
+ return;
+
+ Surface *surface;
+ if (bufferedDraw) {
+ surface = pixmapSelMargin;
+ } else {
+ surface = surfWindow;
+ }
+
+ PRectangle rcSelMargin = rcMargin;
+ rcSelMargin.right = rcMargin.left;
+
+ for (int margin = 0; margin < vs.margins; margin++) {
+ if (vs.ms[margin].width > 0) {
+
+ rcSelMargin.left = rcSelMargin.right;
+ rcSelMargin.right = rcSelMargin.left + vs.ms[margin].width;
+
+ if (vs.ms[margin].style != SC_MARGIN_NUMBER) {
+ /* alternate scheme:
+ if (vs.ms[margin].mask & SC_MASK_FOLDERS)
+ surface->FillRectangle(rcSelMargin, vs.styles[STYLE_DEFAULT].back.allocated);
+ else
+ // Required because of special way brush is created for selection margin
+ surface->FillRectangle(rcSelMargin, pixmapSelPattern);
+ */
+ if (vs.ms[margin].mask & SC_MASK_FOLDERS)
+ // Required because of special way brush is created for selection margin
+ surface->FillRectangle(rcSelMargin, *pixmapSelPattern);
+ else {
+ ColourAllocated colour;
+ switch (vs.ms[margin].style) {
+ case SC_MARGIN_BACK:
+ colour = vs.styles[STYLE_DEFAULT].back.allocated;
+ break;
+ case SC_MARGIN_FORE:
+ colour = vs.styles[STYLE_DEFAULT].fore.allocated;
+ break;
+ default:
+ colour = vs.styles[STYLE_LINENUMBER].back.allocated;
+ break;
+ }
+ surface->FillRectangle(rcSelMargin, colour);
+ }
+ } else {
+ surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back.allocated);
+ }
+
+ int visibleLine = topLine;
+ int yposScreen = 0;
+
+ // Work out whether the top line is whitespace located after a
+ // lessening of fold level which implies a 'fold tail' but which should not
+ // be displayed until the last of a sequence of whitespace.
+ bool needWhiteClosure = false;
+ int level = pdoc->GetLevel(cs.DocFromDisplay(topLine));
+ if (level & SC_FOLDLEVELWHITEFLAG) {
+ int lineBack = cs.DocFromDisplay(topLine);
+ int levelPrev = level;
+ while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) {
+ lineBack--;
+ levelPrev = pdoc->GetLevel(lineBack);
+ }
+ if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
+ if ((level & SC_FOLDLEVELNUMBERMASK) < (levelPrev & SC_FOLDLEVELNUMBERMASK))
+ needWhiteClosure = true;
+ }
+ }
+
+ // Old code does not know about new markers needed to distinguish all cases
+ int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID,
+ SC_MARKNUM_FOLDEROPEN);
+ int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND,
+ SC_MARKNUM_FOLDER);
+
+ while ((visibleLine < cs.LinesDisplayed()) && yposScreen < rcMargin.bottom) {
+
+ PLATFORM_ASSERT(visibleLine < cs.LinesDisplayed());
+
+ int lineDoc = cs.DocFromDisplay(visibleLine);
+ PLATFORM_ASSERT(cs.GetVisible(lineDoc));
+ bool firstSubLine = visibleLine == cs.DisplayFromDoc(lineDoc);
+
+ // Decide which fold indicator should be displayed
+ level = pdoc->GetLevel(lineDoc);
+ int levelNext = pdoc->GetLevel(lineDoc + 1);
+ int marks = pdoc->GetMark(lineDoc);
+ if (!firstSubLine)
+ marks = 0;
+ int levelNum = level & SC_FOLDLEVELNUMBERMASK;
+ int levelNextNum = levelNext & SC_FOLDLEVELNUMBERMASK;
+ if (level & SC_FOLDLEVELHEADERFLAG) {
+ if (firstSubLine) {
+ if (cs.GetExpanded(lineDoc)) {
+ if (levelNum == SC_FOLDLEVELBASE)
+ marks |= 1 << SC_MARKNUM_FOLDEROPEN;
+ else
+ marks |= 1 << folderOpenMid;
+ } else {
+ if (levelNum == SC_FOLDLEVELBASE)
+ marks |= 1 << SC_MARKNUM_FOLDER;
+ else
+ marks |= 1 << folderEnd;
+ }
+ } else {
+ marks |= 1 << SC_MARKNUM_FOLDERSUB;
+ }
+ needWhiteClosure = false;
+ } else if (level & SC_FOLDLEVELWHITEFLAG) {
+ if (needWhiteClosure) {
+ if (levelNext & SC_FOLDLEVELWHITEFLAG) {
+ marks |= 1 << SC_MARKNUM_FOLDERSUB;
+ } else if (levelNum > SC_FOLDLEVELBASE) {
+ marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
+ needWhiteClosure = false;
+ } else {
+ marks |= 1 << SC_MARKNUM_FOLDERTAIL;
+ needWhiteClosure = false;
+ }
+ } else if (levelNum > SC_FOLDLEVELBASE) {
+ if (levelNextNum < levelNum) {
+ if (levelNextNum > SC_FOLDLEVELBASE) {
+ marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
+ } else {
+ marks |= 1 << SC_MARKNUM_FOLDERTAIL;
+ }
+ } else {
+ marks |= 1 << SC_MARKNUM_FOLDERSUB;
+ }
+ }
+ } else if (levelNum > SC_FOLDLEVELBASE) {
+ if (levelNextNum < levelNum) {
+ needWhiteClosure = false;
+ if (levelNext & SC_FOLDLEVELWHITEFLAG) {
+ marks |= 1 << SC_MARKNUM_FOLDERSUB;
+ needWhiteClosure = true;
+ } else if (levelNextNum > SC_FOLDLEVELBASE) {
+ marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
+ } else {
+ marks |= 1 << SC_MARKNUM_FOLDERTAIL;
+ }
+ } else {
+ marks |= 1 << SC_MARKNUM_FOLDERSUB;
+ }
+ }
+
+ marks &= vs.ms[margin].mask;
+ PRectangle rcMarker = rcSelMargin;
+ rcMarker.top = yposScreen;
+ rcMarker.bottom = yposScreen + vs.lineHeight;
+ if (vs.ms[margin].style == SC_MARGIN_NUMBER) {
+ char number[100];
+ number[0] = '\0';
+ if (firstSubLine)
+ sprintf(number, "%d", lineDoc + 1);
+ if (foldFlags & SC_FOLDFLAG_LEVELNUMBERS) {
+ int lev = pdoc->GetLevel(lineDoc);
+ sprintf(number, "%c%c %03X %03X",
+ (lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_',
+ (lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_',
+ lev & SC_FOLDLEVELNUMBERMASK,
+ lev >> 16
+ );
+ }
+ PRectangle rcNumber = rcMarker;
+ // Right justify
+ int width = surface->WidthText(vs.styles[STYLE_LINENUMBER].font, number, istrlen(number));
+ int xpos = rcNumber.right - width - 3;
+ rcNumber.left = xpos;
+ surface->DrawTextNoClip(rcNumber, vs.styles[STYLE_LINENUMBER].font,
+ rcNumber.top + vs.maxAscent, number, istrlen(number),
+ vs.styles[STYLE_LINENUMBER].fore.allocated,
+ vs.styles[STYLE_LINENUMBER].back.allocated);
+ }
+
+ if (marks) {
+ for (int markBit = 0; (markBit < 32) && marks; markBit++) {
+ if (marks & 1) {
+ vs.markers[markBit].Draw(surface, rcMarker, vs.styles[STYLE_LINENUMBER].font);
+ }
+ marks >>= 1;
+ }
+ }
+
+ visibleLine++;
+ yposScreen += vs.lineHeight;
+ }
+ }
+ }
+
+ PRectangle rcBlankMargin = rcMargin;
+ rcBlankMargin.left = rcSelMargin.right;
+ surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back.allocated);
+
+ if (bufferedDraw) {
+ surfWindow->Copy(rcMargin, Point(), *pixmapSelMargin);
+ }
+}
+
+void DrawTabArrow(Surface *surface, PRectangle rcTab, int ymid) {
+ int ydiff = (rcTab.bottom - rcTab.top) / 2;
+ int xhead = rcTab.right - 1 - ydiff;
+ if (xhead <= rcTab.left) {
+ ydiff -= rcTab.left - xhead - 1;
+ xhead = rcTab.left - 1;
+ }
+ if ((rcTab.left + 2) < (rcTab.right - 1))
+ surface->MoveTo(rcTab.left + 2, ymid);
+ else
+ surface->MoveTo(rcTab.right - 1, ymid);
+ surface->LineTo(rcTab.right - 1, ymid);
+ surface->LineTo(xhead, ymid - ydiff);
+ surface->MoveTo(rcTab.right - 1, ymid);
+ surface->LineTo(xhead, ymid + ydiff);
+}
+
+static bool IsSpaceOrTab(char ch) {
+ return ch == ' ' || ch == '\t';
+}
+
+LineLayout *Editor::RetrieveLineLayout(int lineNumber) {
+ int posLineStart = pdoc->LineStart(lineNumber);
+ int posLineEnd = pdoc->LineStart(lineNumber + 1);
+ int lineCaret = pdoc->LineFromPosition(currentPos);
+ return llc.Retrieve(lineNumber, lineCaret,
+ posLineEnd - posLineStart, pdoc->GetStyleClock(),
+ LinesOnScreen() + 1, pdoc->LinesTotal());
+}
+
+/**
+ * Fill in the LineLayout data for the given line.
+ * Copy the given @a line and its styles from the document into local arrays.
+ * Also determine the x position at which each character starts.
+ */
+void Editor::LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll, int width) {
+ if (!ll)
+ return;
+ PLATFORM_ASSERT(line < pdoc->LinesTotal());
+ int posLineStart = pdoc->LineStart(line);
+ int posLineEnd = pdoc->LineStart(line + 1);
+ // If the line is very long, limit the treatment to a length that should fit in the viewport
+ if (posLineEnd > (posLineStart + ll->maxLineLength)) {
+ posLineEnd = posLineStart + ll->maxLineLength;
+ }
+ if (ll->validity == LineLayout::llCheckTextAndStyle) {
+ int lineLength = posLineEnd - posLineStart;
+ if (!vstyle.viewEOL) {
+ int cid = posLineEnd - 1;
+ while ((cid > posLineStart) && IsEOLChar(pdoc->CharAt(cid))) {
+ cid--;
+ lineLength--;
+ }
+ }
+ if (lineLength == ll->numCharsInLine) {
+ // See if chars, styles, indicators, are all the same
+ bool allSame = true;
+ const int styleMask = pdoc->stylingBitsMask;
+ // Check base line layout
+ char styleByte = 0;
+ int numCharsInLine = 0;
+ while (numCharsInLine < lineLength) {
+ int charInDoc = numCharsInLine + posLineStart;
+ char chDoc = pdoc->CharAt(charInDoc);
+ styleByte = pdoc->StyleAt(charInDoc);
+ allSame = allSame &&
+ (ll->styles[numCharsInLine] == static_cast<unsigned char>(styleByte & styleMask));
+ allSame = allSame &&
+ (ll->indicators[numCharsInLine] == static_cast<char>(styleByte & ~styleMask));
+ if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseMixed)
+ allSame = allSame &&
+ (ll->chars[numCharsInLine] == chDoc);
+ else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
+ allSame = allSame &&
+ (ll->chars[numCharsInLine] == static_cast<char>(tolower(chDoc)));
+ else // Style::caseUpper
+ allSame = allSame &&
+ (ll->chars[numCharsInLine] == static_cast<char>(toupper(chDoc)));
+ numCharsInLine++;
+ }
+ allSame = allSame && (ll->styles[numCharsInLine] == styleByte); // For eolFilled
+ if (allSame) {
+ ll->validity = LineLayout::llPositions;
+ } else {
+ ll->validity = LineLayout::llInvalid;
+ }
+ } else {
+ ll->validity = LineLayout::llInvalid;
+ }
+ }
+ if (ll->validity == LineLayout::llInvalid) {
+ ll->widthLine = LineLayout::wrapWidthInfinite;
+ ll->lines = 1;
+ int numCharsInLine = 0;
+ if (vstyle.edgeState == EDGE_BACKGROUND) {
+ ll->edgeColumn = pdoc->FindColumn(line, theEdge);
+ if (ll->edgeColumn >= posLineStart) {
+ ll->edgeColumn -= posLineStart;
+ }
+ } else {
+ ll->edgeColumn = -1;
+ }
+
+ char styleByte = 0;
+ int styleMask = pdoc->stylingBitsMask;
+ ll->styleBitsSet = 0;
+ // Fill base line layout
+ for (int charInDoc = posLineStart; charInDoc < posLineEnd; charInDoc++) {
+ char chDoc = pdoc->CharAt(charInDoc);
+ styleByte = pdoc->StyleAt(charInDoc);
+ ll->styleBitsSet |= styleByte;
+ if (vstyle.viewEOL || (!IsEOLChar(chDoc))) {
+ ll->chars[numCharsInLine] = chDoc;
+ ll->styles[numCharsInLine] = static_cast<char>(styleByte & styleMask);
+ ll->indicators[numCharsInLine] = static_cast<char>(styleByte & ~styleMask);
+ if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseUpper)
+ ll->chars[numCharsInLine] = static_cast<char>(toupper(chDoc));
+ else if (vstyle.styles[ll->styles[numCharsInLine]].caseForce == Style::caseLower)
+ ll->chars[numCharsInLine] = static_cast<char>(tolower(chDoc));
+ numCharsInLine++;
+ }
+ }
+ ll->xHighlightGuide = 0;
+ // Extra element at the end of the line to hold end x position and act as
+ ll->chars[numCharsInLine] = 0; // Also triggers processing in the loops as this is a control character
+ ll->styles[numCharsInLine] = styleByte; // For eolFilled
+ ll->indicators[numCharsInLine] = 0;
+
+ // Layout the line, determining the position of each character,
+ // with an extra element at the end for the end of the line.
+ int startseg = 0; // Start of the current segment, in char. number
+ int startsegx = 0; // Start of the current segment, in pixels
+ ll->positions[0] = 0;
+ unsigned int tabWidth = vstyle.spaceWidth * pdoc->tabInChars;
+ bool lastSegItalics = false;
+ Font &ctrlCharsFont = vstyle.styles[STYLE_CONTROLCHAR].font;
+
+ int ctrlCharWidth[32] = {0};
+ bool isControlNext = IsControlCharacter(ll->chars[0]);
+ for (int charInLine = 0; charInLine < numCharsInLine; charInLine++) {
+ bool isControl = isControlNext;
+ isControlNext = IsControlCharacter(ll->chars[charInLine + 1]);
+ if ((ll->styles[charInLine] != ll->styles[charInLine + 1]) ||
+ isControl || isControlNext) {
+ ll->positions[startseg] = 0;
+ if (vstyle.styles[ll->styles[charInLine]].visible) {
+ if (isControl) {
+ if (ll->chars[charInLine] == '\t') {
+ ll->positions[charInLine + 1] = ((((startsegx + 2) /
+ tabWidth) + 1) * tabWidth) - startsegx;
+ } else if (controlCharSymbol < 32) {
+ if (ctrlCharWidth[ll->chars[charInLine]] == 0) {
+ const char *ctrlChar = ControlCharacterString(ll->chars[charInLine]);
+ // +3 For a blank on front and rounded edge each side:
+ ctrlCharWidth[ll->chars[charInLine]] =
+ surface->WidthText(ctrlCharsFont, ctrlChar, istrlen(ctrlChar)) + 3;
+ }
+ ll->positions[charInLine + 1] = ctrlCharWidth[ll->chars[charInLine]];
+ } else {
+ char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
+ surface->MeasureWidths(ctrlCharsFont, cc, 1,
+ ll->positions + startseg + 1);
+ }
+ lastSegItalics = false;
+ } else { // Regular character
+ int lenSeg = charInLine - startseg + 1;
+ if ((lenSeg == 1) && (' ' == ll->chars[startseg])) {
+ lastSegItalics = false;
+ // Over half the segments are single characters and of these about half are space characters.
+ ll->positions[charInLine + 1] = vstyle.styles[ll->styles[charInLine]].spaceWidth;
+ } else {
+ lastSegItalics = vstyle.styles[ll->styles[charInLine]].italic;
+ surface->MeasureWidths(vstyle.styles[ll->styles[charInLine]].font, ll->chars + startseg,
+ lenSeg, ll->positions + startseg + 1);
+ }
+ }
+ } else { // invisible
+ for (int posToZero = startseg; posToZero <= (charInLine + 1); posToZero++) {
+ ll->positions[posToZero] = 0;
+ }
+ }
+ for (int posToIncrease = startseg; posToIncrease <= (charInLine + 1); posToIncrease++) {
+ ll->positions[posToIncrease] += startsegx;
+ }
+ startsegx = ll->positions[charInLine + 1];
+ startseg = charInLine + 1;
+ }
+ }
+ // Small hack to make lines that end with italics not cut off the edge of the last character
+ if ((startseg > 0) && lastSegItalics) {
+ ll->positions[startseg] += 2;
+ }
+ ll->numCharsInLine = numCharsInLine;
+ ll->validity = LineLayout::llPositions;
+ }
+ // Hard to cope when too narrow, so just assume there is space
+ if (width < 20) {
+ width = 20;
+ }
+ if ((ll->validity == LineLayout::llPositions) || (ll->widthLine != width)) {
+ ll->widthLine = width;
+ if (width == LineLayout::wrapWidthInfinite) {
+ ll->lines = 1;
+ } else if (width > ll->positions[ll->numCharsInLine]) {
+ // Simple common case where line does not need wrapping.
+ ll->lines = 1;
+ } else {
+ if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
+ width -= vstyle.aveCharWidth; // take into account the space for end wrap mark
+ }
+ ll->lines = 0;
+ // Calculate line start positions based upon width.
+ // For now this is simplistic - wraps on byte rather than character and
+ // in the middle of words. Should search for spaces or style changes.
+ int lastGoodBreak = 0;
+ int lastLineStart = 0;
+ int startOffset = 0;
+ int p = 0;
+ while (p < ll->numCharsInLine) {
+ if ((ll->positions[p + 1] - startOffset) >= width) {
+ if (lastGoodBreak == lastLineStart) {
+ // Try moving to start of last character
+ if (p > 0) {
+ lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
+ - posLineStart;
+ }
+ if (lastGoodBreak == lastLineStart) {
+ // Ensure at least one character on line.
+ lastGoodBreak = pdoc->MovePositionOutsideChar(lastGoodBreak + posLineStart + 1, 1)
+ - posLineStart;
+ }
+ }
+ lastLineStart = lastGoodBreak;
+ ll->lines++;
+ ll->SetLineStart(ll->lines, lastGoodBreak);
+ startOffset = ll->positions[lastGoodBreak];
+ // take into account the space for start wrap mark and indent
+ startOffset -= actualWrapVisualStartIndent * vstyle.aveCharWidth;
+ p = lastGoodBreak + 1;
+ continue;
+ }
+ if (p > 0) {
+ if (wrapState == eWrapChar) {
+ lastGoodBreak = pdoc->MovePositionOutsideChar(p + posLineStart, -1)
+ - posLineStart;
+ p = pdoc->MovePositionOutsideChar(p + 1 + posLineStart, 1) - posLineStart;
+ continue;
+ } else if (ll->styles[p] != ll->styles[p - 1]) {
+ lastGoodBreak = p;
+ } else if (IsSpaceOrTab(ll->chars[p - 1]) && !IsSpaceOrTab(ll->chars[p])) {
+ lastGoodBreak = p;
+ }
+ }
+ p++;
+ }
+ ll->lines++;
+ }
+ ll->validity = LineLayout::llLines;
+ }
+}
+
+ColourAllocated Editor::SelectionBackground(ViewStyle &vsDraw) {
+ return primarySelection ? vsDraw.selbackground.allocated : vsDraw.selbackground2.allocated;
+}
+
+ColourAllocated Editor::TextBackground(ViewStyle &vsDraw, bool overrideBackground,
+ ColourAllocated background, bool inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll) {
+ if (inSelection) {
+ if (vsDraw.selbackset && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
+ return SelectionBackground(vsDraw);
+ }
+ } else {
+ if ((vsDraw.edgeState == EDGE_BACKGROUND) &&
+ (i >= ll->edgeColumn) &&
+ !IsEOLChar(ll->chars[i]))
+ return vsDraw.edgecolour.allocated;
+ if (inHotspot && vsDraw.hotspotBackgroundSet)
+ return vsDraw.hotspotBackground.allocated;
+ if (overrideBackground)
+ return background;
+ }
+ return vsDraw.styles[styleMain].back.allocated;
+}
+
+void Editor::DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight) {
+ Point from(0, ((lineVisible & 1) && (lineHeight & 1)) ? 1 : 0);
+ PRectangle rcCopyArea(start + 1, rcSegment.top, start + 2, rcSegment.bottom);
+ surface->Copy(rcCopyArea, from,
+ highlight ? *pixmapIndentGuideHighlight : *pixmapIndentGuide);
+}
+
+void Editor::DrawWrapMarker(Surface *surface, PRectangle rcPlace,
+ bool isEndMarker, ColourAllocated wrapColour) {
+ surface->PenColour(wrapColour);
+
+ enum { xa = 1 }; // gap before start
+ int w = rcPlace.right - rcPlace.left - xa - 1;
+
+ bool xStraight = isEndMarker; // x-mirrored symbol for start marker
+ bool yStraight = true;
+ //bool yStraight= isEndMarker; // comment in for start marker y-mirrowed
+
+ int x0 = xStraight ? rcPlace.left : rcPlace.right - 1;
+ int y0 = yStraight ? rcPlace.top : rcPlace.bottom - 1;
+
+ int dy = (rcPlace.bottom - rcPlace.top) / 5;
+ int y = (rcPlace.bottom - rcPlace.top) / 2 + dy;
+
+ struct Relative {
+ Surface *surface;
+ int xBase;
+ int xDir;
+ int yBase;
+ int yDir;
+ void MoveTo(int xRelative, int yRelative) {
+ surface->MoveTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
+ }
+ void LineTo(int xRelative, int yRelative) {
+ surface->LineTo(xBase + xDir * xRelative, yBase + yDir * yRelative);
+ }
+ };
+ Relative rel = {surface, x0, xStraight ? 1 : -1, y0, yStraight ? 1 : -1};
+
+ // arrow head
+ rel.MoveTo(xa, y);
+ rel.LineTo(xa + 2*w / 3, y - dy);
+ rel.MoveTo(xa, y);
+ rel.LineTo(xa + 2*w / 3, y + dy);
+
+ // arrow body
+ rel.MoveTo(xa, y);
+ rel.LineTo(xa + w, y);
+ rel.LineTo(xa + w, y - 2 * dy);
+ rel.LineTo(xa - 1, // on windows lineto is exclusive endpoint, perhaps GTK not...
+ y - 2 * dy);
+}
+
+static void SimpleAlphaRectangle(Surface *surface, PRectangle rc, ColourAllocated fill, int alpha) {
+ if (alpha != SC_ALPHA_NOALPHA) {
+ surface->AlphaRectangle(rc, 0, fill, alpha, fill, alpha, 0);
+ }
+}
+
+void Editor::DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll,
+ int line, int lineEnd, int xStart, int subLine, int subLineStart,
+ bool overrideBackground, ColourAllocated background,
+ bool drawWrapMarkEnd, ColourAllocated wrapColour) {
+
+ int styleMask = pdoc->stylingBitsMask;
+ PRectangle rcSegment = rcLine;
+
+ // Fill in a PRectangle representing the end of line characters
+ int xEol = ll->positions[lineEnd] - subLineStart;
+ rcSegment.left = xEol + xStart;
+ rcSegment.right = xEol + vsDraw.aveCharWidth + xStart;
+ int posLineEnd = pdoc->LineStart(line + 1);
+ bool eolInSelection = (subLine == (ll->lines - 1)) &&
+ (posLineEnd > ll->selStart) && (posLineEnd <= ll->selEnd) && (ll->selStart != ll->selEnd);
+
+ if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (vsDraw.selAlpha == SC_ALPHA_NOALPHA)) {
+ surface->FillRectangle(rcSegment, SelectionBackground(vsDraw));
+ } else {
+ if (overrideBackground) {
+ surface->FillRectangle(rcSegment, background);
+ } else {
+ surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
+ }
+ if (eolInSelection && vsDraw.selbackset && (line < pdoc->LinesTotal() - 1) && (vsDraw.selAlpha != SC_ALPHA_NOALPHA)) {
+ SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw), vsDraw.selAlpha);
+ }
+ }
+
+ rcSegment.left = xEol + vsDraw.aveCharWidth + xStart;
+ rcSegment.right = rcLine.right;
+ if (overrideBackground) {
+ surface->FillRectangle(rcSegment, background);
+ } else if (vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].eolFilled) {
+ surface->FillRectangle(rcSegment, vsDraw.styles[ll->styles[ll->numCharsInLine] & styleMask].back.allocated);
+ } else {
+ surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated);
+ }
+
+ if (drawWrapMarkEnd) {
+ PRectangle rcPlace = rcSegment;
+
+ if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_END_BY_TEXT) {
+ rcPlace.left = xEol + xStart;
+ rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
+ } else {
+ // draw left of the right text margin, to avoid clipping by the current clip rect
+ rcPlace.right = rcLine.right - vs.rightMarginWidth;
+ rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
+ }
+ DrawWrapMarker(surface, rcPlace, true, wrapColour);
+ }
+}
+
+void Editor::DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart,
+ PRectangle rcLine, LineLayout *ll, int subLine) {
+
+ PRectangle rcSegment = rcLine;
+
+ // Using one font for all control characters so it can be controlled independently to ensure
+ // the box goes around the characters tightly. Seems to be no way to work out what height
+ // is taken by an individual character - internal leading gives varying results.
+ Font &ctrlCharsFont = vsDraw.styles[STYLE_CONTROLCHAR].font;
+
+ // See if something overrides the line background color: Either if caret is on the line
+ // and background color is set for that, or if a marker is defined that forces its background
+ // color onto the line, or if a marker is defined but has no selection margin in which to
+ // display itself (as long as it's not an SC_MARK_EMPTY marker). These are checked in order
+ // with the earlier taking precedence. When multiple markers cause background override,
+ // the color for the highest numbered one is used.
+ bool overrideBackground = false;
+ ColourAllocated background;
+ if (caret.active && vsDraw.showCaretLineBackground && (vsDraw.caretLineAlpha == SC_ALPHA_NOALPHA) && ll->containsCaret) {
+ overrideBackground = true;
+ background = vsDraw.caretLineBackground.allocated;
+ }
+ if (!overrideBackground) {
+ int marks = pdoc->GetMark(line);
+ for (int markBit = 0; (markBit < 32) && marks; markBit++) {
+ if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND) &&
+ (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
+ background = vsDraw.markers[markBit].back.allocated;
+ overrideBackground = true;
+ }
+ marks >>= 1;
+ }
+ }
+ if (!overrideBackground) {
+ if (vsDraw.maskInLine) {
+ int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
+ if (marksMasked) {
+ for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
+ if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY) &&
+ (vsDraw.markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
+ overrideBackground = true;
+ background = vsDraw.markers[markBit].back.allocated;
+ }
+ marksMasked >>= 1;
+ }
+ }
+ }
+ }
+
+ bool drawWhitespaceBackground = (vsDraw.viewWhitespace != wsInvisible) &&
+ (!overrideBackground) && (vsDraw.whitespaceBackgroundSet);
+
+ bool inIndentation = subLine == 0; // Do not handle indentation except on first subline.
+ int indentWidth = pdoc->IndentSize() * vsDraw.spaceWidth;
+
+ int posLineStart = pdoc->LineStart(line);
+
+ int startseg = ll->LineStart(subLine);
+ int subLineStart = ll->positions[startseg];
+ int lineStart = 0;
+ int lineEnd = 0;
+ if (subLine < ll->lines) {
+ lineStart = ll->LineStart(subLine);
+ lineEnd = ll->LineStart(subLine + 1);
+ }
+
+ bool drawWrapMarkEnd = false;
+
+ if (wrapVisualFlags & SC_WRAPVISUALFLAG_END) {
+ if (subLine + 1 < ll->lines) {
+ drawWrapMarkEnd = ll->LineStart(subLine + 1) != 0;
+ }
+ }
+
+ if (actualWrapVisualStartIndent != 0) {
+
+ bool continuedWrapLine = false;
+ if (subLine < ll->lines) {
+ continuedWrapLine = ll->LineStart(subLine) != 0;
+ }
+
+ if (continuedWrapLine) {
+ // draw continuation rect
+ PRectangle rcPlace = rcSegment;
+
+ rcPlace.left = ll->positions[startseg] + xStart - subLineStart;
+ rcPlace.right = rcPlace.left + actualWrapVisualStartIndent * vsDraw.aveCharWidth;
+
+ // default bgnd here..
+ surface->FillRectangle(rcSegment, vsDraw.styles[STYLE_DEFAULT].back.allocated);
+
+ // main line style would be below but this would be inconsistent with end markers
+ // also would possibly not be the style at wrap point
+ //int styleMain = ll->styles[lineStart];
+ //surface->FillRectangle(rcPlace, vsDraw.styles[styleMain].back.allocated);
+
+ if (wrapVisualFlags & SC_WRAPVISUALFLAG_START) {
+
+ if (wrapVisualFlagsLocation & SC_WRAPVISUALFLAGLOC_START_BY_TEXT)
+ rcPlace.left = rcPlace.right - vsDraw.aveCharWidth;
+ else
+ rcPlace.right = rcPlace.left + vsDraw.aveCharWidth;
+
+ DrawWrapMarker(surface, rcPlace, false, vsDraw.whitespaceForeground.allocated);
+ }
+
+ xStart += actualWrapVisualStartIndent * vsDraw.aveCharWidth;
+ }
+ }
+
+ int i;
+
+ // Background drawing loop
+ for (i = lineStart; twoPhaseDraw && (i < lineEnd); i++) {
+
+ int iDoc = i + posLineStart;
+ // If there is the end of a style run for any reason
+ if ((ll->styles[i] != ll->styles[i + 1]) ||
+ i == (lineEnd - 1) ||
+ IsControlCharacter(ll->chars[i]) || IsControlCharacter(ll->chars[i + 1]) ||
+ ((ll->selStart != ll->selEnd) && ((iDoc + 1 == ll->selStart) || (iDoc + 1 == ll->selEnd))) ||
+ (i == (ll->edgeColumn - 1))) {
+ rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
+ rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
+ // Only try to draw if really visible - enhances performance by not calling environment to
+ // draw strings that are completely past the right side of the window.
+ if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
+ int styleMain = ll->styles[i];
+ bool inSelection = (iDoc >= ll->selStart) && (iDoc < ll->selEnd) && (ll->selStart != ll->selEnd);
+ bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
+ ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
+ if (ll->chars[i] == '\t') {
+ // Tab display
+ if (drawWhitespaceBackground &&
+ (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
+ textBack = vsDraw.whitespaceBackground.allocated;
+ surface->FillRectangle(rcSegment, textBack);
+ } else if (IsControlCharacter(ll->chars[i])) {
+ // Control character display
+ inIndentation = false;
+ surface->FillRectangle(rcSegment, textBack);
+ } else {
+ // Normal text display
+ surface->FillRectangle(rcSegment, textBack);
+ if (vsDraw.viewWhitespace != wsInvisible ||
+ (inIndentation && vsDraw.viewIndentationGuides)) {
+ for (int cpos = 0; cpos <= i - startseg; cpos++) {
+ if (ll->chars[cpos + startseg] == ' ') {
+ if (drawWhitespaceBackground &&
+ (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
+ PRectangle rcSpace(ll->positions[cpos + startseg] + xStart, rcSegment.top,
+ ll->positions[cpos + startseg + 1] + xStart, rcSegment.bottom);
+ surface->FillRectangle(rcSpace, vsDraw.whitespaceBackground.allocated);
+ }
+ } else {
+ inIndentation = false;
+ }
+ }
+ }
+ }
+ } else if (rcSegment.left > rcLine.right) {
+ break;
+ }
+ startseg = i + 1;
+ }
+ }
+
+ if (twoPhaseDraw) {
+ DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
+ xStart, subLine, subLineStart, overrideBackground, background,
+ drawWrapMarkEnd, vsDraw.whitespaceForeground.allocated);
+ }
+
+ inIndentation = subLine == 0; // Do not handle indentation except on first subline.
+ startseg = ll->LineStart(subLine);
+ // Foreground drawing loop
+ for (i = lineStart; i < lineEnd; i++) {
+
+ int iDoc = i + posLineStart;
+ // If there is the end of a style run for any reason
+ if ((ll->styles[i] != ll->styles[i + 1]) ||
+ i == (lineEnd - 1) ||
+ IsControlCharacter(ll->chars[i]) || IsControlCharacter(ll->chars[i + 1]) ||
+ ((ll->selStart != ll->selEnd) && ((iDoc + 1 == ll->selStart) || (iDoc + 1 == ll->selEnd))) ||
+ (i == (ll->edgeColumn - 1))) {
+ rcSegment.left = ll->positions[startseg] + xStart - subLineStart;
+ rcSegment.right = ll->positions[i + 1] + xStart - subLineStart;
+ // Only try to draw if really visible - enhances performance by not calling environment to
+ // draw strings that are completely past the right side of the window.
+ if ((rcSegment.left <= rcLine.right) && (rcSegment.right >= rcLine.left)) {
+ int styleMain = ll->styles[i];
+ ColourAllocated textFore = vsDraw.styles[styleMain].fore.allocated;
+ Font &textFont = vsDraw.styles[styleMain].font;
+ //hotspot foreground
+ if (ll->hsStart != -1 && iDoc >= ll->hsStart && iDoc < hsEnd) {
+ if (vsDraw.hotspotForegroundSet)
+ textFore = vsDraw.hotspotForeground.allocated;
+ }
+ bool inSelection = (iDoc >= ll->selStart) && (iDoc < ll->selEnd) && (ll->selStart != ll->selEnd);
+ if (inSelection && (vsDraw.selforeset)) {
+ textFore = vsDraw.selforeground.allocated;
+ }
+ bool inHotspot = (ll->hsStart != -1) && (iDoc >= ll->hsStart) && (iDoc < ll->hsEnd);
+ ColourAllocated textBack = TextBackground(vsDraw, overrideBackground, background, inSelection, inHotspot, styleMain, i, ll);
+ if (ll->chars[i] == '\t') {
+ // Tab display
+ if (!twoPhaseDraw) {
+ if (drawWhitespaceBackground &&
+ (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways))
+ textBack = vsDraw.whitespaceBackground.allocated;
+ surface->FillRectangle(rcSegment, textBack);
+ }
+ if ((vsDraw.viewWhitespace != wsInvisible) || ((inIndentation && vsDraw.viewIndentationGuides))) {
+ if (vsDraw.whitespaceForegroundSet)
+ textFore = vsDraw.whitespaceForeground.allocated;
+ surface->PenColour(textFore);
+ }
+ if (inIndentation && vsDraw.viewIndentationGuides) {
+ for (int xIG = ll->positions[i] / indentWidth * indentWidth; xIG < ll->positions[i + 1]; xIG += indentWidth) {
+ if (xIG >= ll->positions[i] && xIG > 0) {
+ DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, xIG + xStart, rcSegment,
+ (ll->xHighlightGuide == xIG));
+ }
+ }
+ }
+ if (vsDraw.viewWhitespace != wsInvisible) {
+ if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
+ PRectangle rcTab(rcSegment.left + 1, rcSegment.top + 4,
+ rcSegment.right - 1, rcSegment.bottom - vsDraw.maxDescent);
+ DrawTabArrow(surface, rcTab, rcSegment.top + vsDraw.lineHeight / 2);
+ }
+ }
+ } else if (IsControlCharacter(ll->chars[i])) {
+ // Control character display
+ inIndentation = false;
+ if (controlCharSymbol < 32) {
+ // Draw the character
+ const char *ctrlChar = ControlCharacterString(ll->chars[i]);
+ if (!twoPhaseDraw) {
+ surface->FillRectangle(rcSegment, textBack);
+ }
+ int normalCharHeight = surface->Ascent(ctrlCharsFont) -
+ surface->InternalLeading(ctrlCharsFont);
+ PRectangle rcCChar = rcSegment;
+ rcCChar.left = rcCChar.left + 1;
+ rcCChar.top = rcSegment.top + vsDraw.maxAscent - normalCharHeight;
+ rcCChar.bottom = rcSegment.top + vsDraw.maxAscent + 1;
+ PRectangle rcCentral = rcCChar;
+ rcCentral.top++;
+ rcCentral.bottom--;
+ surface->FillRectangle(rcCentral, textFore);
+ PRectangle rcChar = rcCChar;
+ rcChar.left++;
+ rcChar.right--;
+ surface->DrawTextClipped(rcChar, ctrlCharsFont,
+ rcSegment.top + vsDraw.maxAscent, ctrlChar, istrlen(ctrlChar),
+ textBack, textFore);
+ } else {
+ char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
+ surface->DrawTextNoClip(rcSegment, ctrlCharsFont,
+ rcSegment.top + vsDraw.maxAscent,
+ cc, 1, textBack, textFore);
+ }
+ } else {
+ // Normal text display
+ if (vsDraw.styles[styleMain].visible) {
+ if (twoPhaseDraw) {
+ surface->DrawTextTransparent(rcSegment, textFont,
+ rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
+ i - startseg + 1, textFore);
+ } else {
+ surface->DrawTextNoClip(rcSegment, textFont,
+ rcSegment.top + vsDraw.maxAscent, ll->chars + startseg,
+ i - startseg + 1, textFore, textBack);
+ }
+ }
+ if (vsDraw.viewWhitespace != wsInvisible ||
+ (inIndentation && vsDraw.viewIndentationGuides)) {
+ for (int cpos = 0; cpos <= i - startseg; cpos++) {
+ if (ll->chars[cpos + startseg] == ' ') {
+ if (vsDraw.viewWhitespace != wsInvisible) {
+ if (vsDraw.whitespaceForegroundSet)
+ textFore = vsDraw.whitespaceForeground.allocated;
+ if (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways) {
+ int xmid = (ll->positions[cpos + startseg] + ll->positions[cpos + startseg + 1]) / 2;
+ if (!twoPhaseDraw && drawWhitespaceBackground &&
+ (!inIndentation || vsDraw.viewWhitespace == wsVisibleAlways)) {
+ textBack = vsDraw.whitespaceBackground.allocated;
+ PRectangle rcSpace(ll->positions[cpos + startseg] + xStart, rcSegment.top, ll->positions[cpos + startseg + 1] + xStart, rcSegment.bottom);
+ surface->FillRectangle(rcSpace, textBack);
+ }
+ PRectangle rcDot(xmid + xStart - subLineStart, rcSegment.top + vsDraw.lineHeight / 2, 0, 0);
+ rcDot.right = rcDot.left + 1;
+ rcDot.bottom = rcDot.top + 1;
+ surface->FillRectangle(rcDot, textFore);
+ }
+ }
+ if (inIndentation && vsDraw.viewIndentationGuides) {
+ int startSpace = ll->positions[cpos + startseg];
+ if (startSpace > 0 && (startSpace % indentWidth == 0)) {
+ DrawIndentGuide(surface, lineVisible, vsDraw.lineHeight, startSpace + xStart, rcSegment,
+ (ll->xHighlightGuide == ll->positions[cpos + startseg]));
+ }
+ }
+ } else {
+ inIndentation = false;
+ }
+ }
+ }
+ }
+ if (ll->hsStart != -1 && vsDraw.hotspotUnderline && iDoc >= ll->hsStart && iDoc < ll->hsEnd ) {
+ PRectangle rcUL = rcSegment;
+ rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
+ rcUL.bottom = rcUL.top + 1;
+ if (vsDraw.hotspotForegroundSet)
+ surface->FillRectangle(rcUL, vsDraw.hotspotForeground.allocated);
+ else
+ surface->FillRectangle(rcUL, textFore);
+ } else if (vsDraw.styles[styleMain].underline) {
+ PRectangle rcUL = rcSegment;
+ rcUL.top = rcUL.top + vsDraw.maxAscent + 1;
+ rcUL.bottom = rcUL.top + 1;
+ surface->FillRectangle(rcUL, textFore);
+ }
+ } else if (rcSegment.left > rcLine.right) {
+ break;
+ }
+ startseg = i + 1;
+ }
+ }
+
+ // Draw indicators
+ // foreach indicator...
+ for (int indicnum = 0, mask = 1 << pdoc->stylingBits; mask < 0x100; indicnum++) {
+ if (!(mask & ll->styleBitsSet)) {
+ mask <<= 1;
+ continue;
+ }
+ int startPos = -1;
+ // foreach style pos in line...
+ for (int indicPos = lineStart; indicPos <= lineEnd; indicPos++) {
+ // look for starts...
+ if (startPos < 0) {
+ // NOT in indicator run, looking for START
+ if (indicPos < lineEnd && (ll->indicators[indicPos] & mask))
+ startPos = indicPos;
+ }
+ // ... or ends
+ if (startPos >= 0) {
+ // IN indicator run, looking for END
+ if (indicPos >= lineEnd || !(ll->indicators[indicPos] & mask)) {
+ // AT end of indicator run, DRAW it!
+ PRectangle rcIndic(
+ ll->positions[startPos] + xStart - subLineStart,
+ rcLine.top + vsDraw.maxAscent,
+ ll->positions[indicPos] + xStart - subLineStart,
+ rcLine.top + vsDraw.maxAscent + 3);
+ vsDraw.indicators[indicnum].Draw(surface, rcIndic, rcLine);
+ // RESET control var
+ startPos = -1;
+ }
+ }
+ }
+ mask <<= 1;
+ }
+ // End of the drawing of the current line
+ if (!twoPhaseDraw) {
+ DrawEOL(surface, vsDraw, rcLine, ll, line, lineEnd,
+ xStart, subLine, subLineStart, overrideBackground, background,
+ drawWrapMarkEnd, vsDraw.whitespaceForeground.allocated);
+ }
+ if ((vsDraw.selAlpha != SC_ALPHA_NOALPHA) && (ll->selStart >= 0) && (ll->selEnd >= 0)) {
+ int startPosSel = (ll->selStart < posLineStart) ? posLineStart : ll->selStart;
+ int endPosSel = (ll->selEnd < (lineEnd + posLineStart)) ? ll->selEnd : (lineEnd + posLineStart);
+ if (startPosSel < endPosSel) {
+ rcSegment.left = xStart + ll->positions[startPosSel - posLineStart] - subLineStart;
+ rcSegment.right = xStart + ll->positions[endPosSel - posLineStart] - subLineStart;
+ SimpleAlphaRectangle(surface, rcSegment, SelectionBackground(vsDraw), vsDraw.selAlpha);
+ }
+ }
+
+ if (vsDraw.edgeState == EDGE_LINE) {
+ int edgeX = theEdge * vsDraw.spaceWidth;
+ rcSegment.left = edgeX + xStart;
+ rcSegment.right = rcSegment.left + 1;
+ surface->FillRectangle(rcSegment, vsDraw.edgecolour.allocated);
+ }
+
+ // Draw any translucent whole line states
+ rcSegment.left = xStart;
+ rcSegment.right = rcLine.right - 1;
+ if (caret.active && vsDraw.showCaretLineBackground && ll->containsCaret) {
+ SimpleAlphaRectangle(surface, rcSegment, vsDraw.caretLineBackground.allocated, vsDraw.caretLineAlpha);
+ }
+ int marks = pdoc->GetMark(line);
+ for (int markBit = 0; (markBit < 32) && marks; markBit++) {
+ if ((marks & 1) && (vsDraw.markers[markBit].markType == SC_MARK_BACKGROUND)) {
+ SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
+ }
+ marks >>= 1;
+ }
+ if (vsDraw.maskInLine) {
+ int marksMasked = pdoc->GetMark(line) & vsDraw.maskInLine;
+ if (marksMasked) {
+ for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
+ if ((marksMasked & 1) && (vsDraw.markers[markBit].markType != SC_MARK_EMPTY)) {
+ SimpleAlphaRectangle(surface, rcSegment, vsDraw.markers[markBit].back.allocated, vsDraw.markers[markBit].alpha);
+ }
+ marksMasked >>= 1;
+ }
+ }
+ }
+}
+
+void Editor::RefreshPixMaps(Surface *surfaceWindow) {
+ if (!pixmapSelPattern->Initialised()) {
+ const int patternSize = 8;
+ pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wMain.GetID());
+ // This complex procedure is to reproduce the checkerboard dithered pattern used by windows
+ // for scroll bars and Visual Studio for its selection margin. The colour of this pattern is half
+ // way between the chrome colour and the chrome highlight colour making a nice transition
+ // between the window chrome and the content area. And it works in low colour depths.
+ PRectangle rcPattern(0, 0, patternSize, patternSize);
+
+ // Initialize default colours based on the chrome colour scheme. Typically the highlight is white.
+ ColourAllocated colourFMFill = vs.selbar.allocated;
+ ColourAllocated colourFMStripes = vs.selbarlight.allocated;
+
+ if (!(vs.selbarlight.desired == ColourDesired(0xff, 0xff, 0xff))) {
+ // User has chosen an unusual chrome colour scheme so just use the highlight edge colour.
+ // (Typically, the highlight colour is white.)
+ colourFMFill = vs.selbarlight.allocated;
+ }
+
+ if (vs.foldmarginColourSet) {
+ // override default fold margin colour
+ colourFMFill = vs.foldmarginColour.allocated;
+ }
+ if (vs.foldmarginHighlightColourSet) {
+ // override default fold margin highlight colour
+ colourFMStripes = vs.foldmarginHighlightColour.allocated;
+ }
+
+ pixmapSelPattern->FillRectangle(rcPattern, colourFMFill);
+ pixmapSelPattern->PenColour(colourFMStripes);
+ for (int stripe = 0; stripe < patternSize; stripe++) {
+ // Alternating 1 pixel stripes is same as checkerboard.
+ pixmapSelPattern->MoveTo(0, stripe * 2);
+ pixmapSelPattern->LineTo(patternSize, stripe * 2 - patternSize);
+ }
+ }
+
+ if (!pixmapIndentGuide->Initialised()) {
+ // 1 extra pixel in height so can handle odd/even positions and so produce a continuous line
+ pixmapIndentGuide->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
+ pixmapIndentGuideHighlight->InitPixMap(1, vs.lineHeight + 1, surfaceWindow, wMain.GetID());
+ PRectangle rcIG(0, 0, 1, vs.lineHeight);
+ pixmapIndentGuide->FillRectangle(rcIG, vs.styles[STYLE_INDENTGUIDE].back.allocated);
+ pixmapIndentGuide->PenColour(vs.styles[STYLE_INDENTGUIDE].fore.allocated);
+ pixmapIndentGuideHighlight->FillRectangle(rcIG, vs.styles[STYLE_BRACELIGHT].back.allocated);
+ pixmapIndentGuideHighlight->PenColour(vs.styles[STYLE_BRACELIGHT].fore.allocated);
+ for (int stripe = 1; stripe < vs.lineHeight + 1; stripe += 2) {
+ pixmapIndentGuide->MoveTo(0, stripe);
+ pixmapIndentGuide->LineTo(2, stripe);
+ pixmapIndentGuideHighlight->MoveTo(0, stripe);
+ pixmapIndentGuideHighlight->LineTo(2, stripe);
+ }
+ }
+
+ if (bufferedDraw) {
+ if (!pixmapLine->Initialised()) {
+ PRectangle rcClient = GetClientRectangle();
+ pixmapLine->InitPixMap(rcClient.Width(), vs.lineHeight,
+ surfaceWindow, wMain.GetID());
+ pixmapSelMargin->InitPixMap(vs.fixedColumnWidth,
+ rcClient.Height(), surfaceWindow, wMain.GetID());
+ }
+ }
+}
+
+void Editor::Paint(Surface *surfaceWindow, PRectangle rcArea) {
+ //Platform::DebugPrintf("Paint:%1d (%3d,%3d) ... (%3d,%3d)\n",
+ // paintingAllText, rcArea.left, rcArea.top, rcArea.right, rcArea.bottom);
+
+ RefreshStyleData();
+ RefreshPixMaps(surfaceWindow);
+
+ PRectangle rcClient = GetClientRectangle();
+ //Platform::DebugPrintf("Client: (%3d,%3d) ... (%3d,%3d) %d\n",
+ // rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
+
+ surfaceWindow->SetPalette(&palette, true);
+ pixmapLine->SetPalette(&palette, !hasFocus);
+
+ int screenLinePaintFirst = rcArea.top / vs.lineHeight;
+ // The area to be painted plus one extra line is styled.
+ // The extra line is to determine when a style change, such as starting a comment flows on to other lines.
+ int lineStyleLast = topLine + (rcArea.bottom - 1) / vs.lineHeight + 1;
+ //Platform::DebugPrintf("Paint lines = %d .. %d\n", topLine + screenLinePaintFirst, lineStyleLast);
+ int endPosPaint = pdoc->Length();
+ if (lineStyleLast < cs.LinesDisplayed())
+ endPosPaint = pdoc->LineStart(cs.DocFromDisplay(lineStyleLast + 1));
+
+ int xStart = vs.fixedColumnWidth - xOffset;
+ int ypos = 0;
+ if (!bufferedDraw)
+ ypos += screenLinePaintFirst * vs.lineHeight;
+ int yposScreen = screenLinePaintFirst * vs.lineHeight;
+
+ // Ensure we are styled as far as we are painting.
+ pdoc->EnsureStyledTo(endPosPaint);
+ bool paintAbandonedByStyling = paintState == paintAbandoned;
+ if (needUpdateUI) {
+ NotifyUpdateUI();
+ needUpdateUI = false;
+ RefreshStyleData();
+ RefreshPixMaps(surfaceWindow);
+ }
+
+ // Call priority lines wrap on a window of lines which are likely
+ // to rendered with the following paint (that is wrap the visible
+ // lines first).
+ int startLineToWrap = cs.DocFromDisplay(topLine) - 5;
+ if (startLineToWrap < 0)
+ startLineToWrap = -1;
+ if (WrapLines(false, startLineToWrap)) {
+ // The wrapping process has changed the height of some lines so
+ // abandon this paint for a complete repaint.
+ if (AbandonPaint()) {
+ return;
+ }
+ RefreshPixMaps(surfaceWindow); // In case pixmaps invalidated by scrollbar change
+ }
+ PLATFORM_ASSERT(pixmapSelPattern->Initialised());
+
+ PaintSelMargin(surfaceWindow, rcArea);
+
+ PRectangle rcRightMargin = rcClient;
+ rcRightMargin.left = rcRightMargin.right - vs.rightMarginWidth;
+ if (rcArea.Intersects(rcRightMargin)) {
+ surfaceWindow->FillRectangle(rcRightMargin, vs.styles[STYLE_DEFAULT].back.allocated);
+ }
+
+ if (paintState == paintAbandoned) {
+ // Either styling or NotifyUpdateUI noticed that painting is needed
+ // outside the current painting rectangle
+ //Platform::DebugPrintf("Abandoning paint\n");
+ if (wrapState != eWrapNone) {
+ if (paintAbandonedByStyling) {
+ // Styling has spilled over a line end, such as occurs by starting a multiline
+ // comment. The width of subsequent text may have changed, so rewrap.
+ NeedWrapping(cs.DocFromDisplay(topLine));
+ }
+ }
+ return;
+ }
+ //Platform::DebugPrintf("start display %d, offset = %d\n", pdoc->Length(), xOffset);
+
+ // Do the painting
+ if (rcArea.right > vs.fixedColumnWidth) {
+
+ Surface *surface = surfaceWindow;
+ if (bufferedDraw) {
+ surface = pixmapLine;
+ PLATFORM_ASSERT(pixmapLine->Initialised());
+ }
+ surface->SetUnicodeMode(IsUnicodeMode());
+ surface->SetDBCSMode(CodePage());
+
+ int visibleLine = topLine + screenLinePaintFirst;
+
+ int posCaret = currentPos;
+ if (posDrag >= 0)
+ posCaret = posDrag;
+ int lineCaret = pdoc->LineFromPosition(posCaret);
+
+ // Remove selection margin from drawing area so text will not be drawn
+ // on it in unbuffered mode.
+ PRectangle rcTextArea = rcClient;
+ rcTextArea.left = vs.fixedColumnWidth;
+ rcTextArea.right -= vs.rightMarginWidth;
+ surfaceWindow->SetClip(rcTextArea);
+
+ // Loop on visible lines
+ //double durLayout = 0.0;
+ //double durPaint = 0.0;
+ //double durCopy = 0.0;
+ //ElapsedTime etWhole;
+ int lineDocPrevious = -1; // Used to avoid laying out one document line multiple times
+ AutoLineLayout ll(llc, 0);
+ SelectionLineIterator lineIterator(this);
+ while (visibleLine < cs.LinesDisplayed() && yposScreen < rcArea.bottom) {
+
+ int lineDoc = cs.DocFromDisplay(visibleLine);
+ // Only visible lines should be handled by the code within the loop
+ PLATFORM_ASSERT(cs.GetVisible(lineDoc));
+ int lineStartSet = cs.DisplayFromDoc(lineDoc);
+ int subLine = visibleLine - lineStartSet;
+
+ // Copy this line and its styles from the document into local arrays
+ // and determine the x position at which each character starts.
+ //ElapsedTime et;
+ if (lineDoc != lineDocPrevious) {
+ ll.Set(0);
+ // For rectangular selection this accesses the layout cache so should be after layout returned.
+ lineIterator.SetAt(lineDoc);
+ ll.Set(RetrieveLineLayout(lineDoc));
+ LayoutLine(lineDoc, surface, vs, ll, wrapWidth);
+ lineDocPrevious = lineDoc;
+ }
+ //durLayout += et.Duration(true);
+
+ if (ll) {
+ if (selType == selStream) {
+ ll->selStart = SelectionStart();
+ ll->selEnd = SelectionEnd();
+ } else {
+ ll->selStart = lineIterator.startPos;
+ ll->selEnd = lineIterator.endPos;
+ }
+ ll->containsCaret = lineDoc == lineCaret;
+ if (hideSelection) {
+ ll->selStart = -1;
+ ll->selEnd = -1;
+ ll->containsCaret = false;
+ }
+
+ GetHotSpotRange(ll->hsStart, ll->hsEnd);
+
+ PRectangle rcLine = rcClient;
+ rcLine.top = ypos;
+ rcLine.bottom = ypos + vs.lineHeight;
+
+ Range rangeLine(pdoc->LineStart(lineDoc), pdoc->LineStart(lineDoc + 1));
+ // Highlight the current braces if any
+ ll->SetBracesHighlight(rangeLine, braces, static_cast<char>(bracesMatchStyle),
+ highlightGuideColumn * vs.spaceWidth);
+
+ // Draw the line
+ DrawLine(surface, vs, lineDoc, visibleLine, xStart, rcLine, ll, subLine);
+ //durPaint += et.Duration(true);
+
+ // Restore the previous styles for the brace highlights in case layout is in cache.
+ ll->RestoreBracesHighlight(rangeLine, braces);
+
+ bool expanded = cs.GetExpanded(lineDoc);
+ if ((foldFlags & SC_FOLDFLAG_BOX) == 0) {
+ // Paint the line above the fold
+ if ((expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_EXPANDED))
+ ||
+ (!expanded && (foldFlags & SC_FOLDFLAG_LINEBEFORE_CONTRACTED))) {
+ if (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELHEADERFLAG) {
+ PRectangle rcFoldLine = rcLine;
+ rcFoldLine.bottom = rcFoldLine.top + 1;
+ surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
+ }
+ }
+ // Paint the line below the fold
+ if ((expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_EXPANDED))
+ ||
+ (!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
+ if (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELHEADERFLAG) {
+ PRectangle rcFoldLine = rcLine;
+ rcFoldLine.top = rcFoldLine.bottom - 1;
+ surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
+ }
+ }
+ } else {
+ int FoldLevelCurr = (pdoc->GetLevel(lineDoc) & SC_FOLDLEVELNUMBERMASK) - SC_FOLDLEVELBASE;
+ int FoldLevelPrev = (pdoc->GetLevel(lineDoc - 1) & SC_FOLDLEVELNUMBERMASK) - SC_FOLDLEVELBASE;
+ int FoldLevelFlags = (pdoc->GetLevel(lineDoc) & ~SC_FOLDLEVELNUMBERMASK) & ~(0xFFF0000);
+ int indentationStep = pdoc->IndentSize();
+ // Draw line above fold
+ if ((FoldLevelPrev < FoldLevelCurr)
+ ||
+ (FoldLevelFlags & SC_FOLDLEVELBOXHEADERFLAG
+ &&
+ (pdoc->GetLevel(lineDoc - 1) & SC_FOLDLEVELBOXFOOTERFLAG) == 0)) {
+ PRectangle rcFoldLine = rcLine;
+ rcFoldLine.bottom = rcFoldLine.top + 1;
+ rcFoldLine.left += xStart + FoldLevelCurr * vs.spaceWidth * indentationStep - 1;
+ surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
+ }
+
+ // Line below the fold (or below a contracted fold)
+ if (FoldLevelFlags & SC_FOLDLEVELBOXFOOTERFLAG
+ ||
+ (!expanded && (foldFlags & SC_FOLDFLAG_LINEAFTER_CONTRACTED))) {
+ PRectangle rcFoldLine = rcLine;
+ rcFoldLine.top = rcFoldLine.bottom - 1;
+ rcFoldLine.left += xStart + (FoldLevelCurr) * vs.spaceWidth * indentationStep - 1;
+ surface->FillRectangle(rcFoldLine, vs.styles[STYLE_DEFAULT].fore.allocated);
+ }
+
+ PRectangle rcBoxLine = rcLine;
+ // Draw vertical line for every fold level
+ for (int i = 0; i <= FoldLevelCurr; i++) {
+ rcBoxLine.left = xStart + i * vs.spaceWidth * indentationStep - 1;
+ rcBoxLine.right = rcBoxLine.left + 1;
+ surface->FillRectangle(rcBoxLine, vs.styles[STYLE_DEFAULT].fore.allocated);
+ }
+ }
+
+ // Draw the Caret
+ if (lineDoc == lineCaret) {
+ int offset = Platform::Minimum(posCaret - rangeLine.start, ll->maxLineLength);
+ if ((offset >= ll->LineStart(subLine)) &&
+ ((offset < ll->LineStart(subLine + 1)) || offset == ll->numCharsInLine)) {
+ int xposCaret = ll->positions[offset] - ll->positions[ll->LineStart(subLine)] + xStart;
+
+ if (actualWrapVisualStartIndent != 0) {
+ int lineStart = ll->LineStart(subLine);
+ if (lineStart != 0) // Wrapped
+ xposCaret += actualWrapVisualStartIndent * vs.aveCharWidth;
+ }
+ int widthOverstrikeCaret;
+ if (posCaret == pdoc->Length()) { // At end of document
+ widthOverstrikeCaret = vs.aveCharWidth;
+ } else if ((posCaret - rangeLine.start) >= ll->numCharsInLine) { // At end of line
+ widthOverstrikeCaret = vs.aveCharWidth;
+ } else {
+ widthOverstrikeCaret = ll->positions[offset + 1] - ll->positions[offset];
+ }
+ if (widthOverstrikeCaret < 3) // Make sure its visible
+ widthOverstrikeCaret = 3;
+ if (((caret.active && caret.on) || (posDrag >= 0)) && xposCaret >= 0) {
+ PRectangle rcCaret = rcLine;
+ int caretWidthOffset = 0;
+ if ((offset > 0) && (vs.caretWidth > 1))
+ caretWidthOffset = 1; // Move back so overlaps both character cells.
+ if (posDrag >= 0) {
+ rcCaret.left = xposCaret - caretWidthOffset;
+ rcCaret.right = rcCaret.left + vs.caretWidth;
+ } else {
+ if (inOverstrike) {
+ rcCaret.top = rcCaret.bottom - 2;
+ rcCaret.left = xposCaret + 1;
+ rcCaret.right = rcCaret.left + widthOverstrikeCaret - 1;
+ } else {
+ rcCaret.left = xposCaret - caretWidthOffset;
+ rcCaret.right = rcCaret.left + vs.caretWidth;
+ }
+ }
+ surface->FillRectangle(rcCaret, vs.caretcolour.allocated);
+ }
+ }
+ }
+
+ if (bufferedDraw) {
+ Point from(vs.fixedColumnWidth, 0);
+ PRectangle rcCopyArea(vs.fixedColumnWidth, yposScreen,
+ rcClient.right, yposScreen + vs.lineHeight);
+ surfaceWindow->Copy(rcCopyArea, from, *pixmapLine);
+ }
+ //durCopy += et.Duration(true);
+ }
+
+ if (!bufferedDraw) {
+ ypos += vs.lineHeight;
+ }
+
+ yposScreen += vs.lineHeight;
+ visibleLine++;
+ //gdk_flush();
+ }
+ ll.Set(0);
+ //if (durPaint < 0.00000001)
+ // durPaint = 0.00000001;
+
+ // Right column limit indicator
+ PRectangle rcBeyondEOF = rcClient;
+ rcBeyondEOF.left = vs.fixedColumnWidth;
+ rcBeyondEOF.right = rcBeyondEOF.right;
+ rcBeyondEOF.top = (cs.LinesDisplayed() - topLine) * vs.lineHeight;
+ if (rcBeyondEOF.top < rcBeyondEOF.bottom) {
+ surfaceWindow->FillRectangle(rcBeyondEOF, vs.styles[STYLE_DEFAULT].back.allocated);
+ if (vs.edgeState == EDGE_LINE) {
+ int edgeX = theEdge * vs.spaceWidth;
+ rcBeyondEOF.left = edgeX + xStart;
+ rcBeyondEOF.right = rcBeyondEOF.left + 1;
+ surfaceWindow->FillRectangle(rcBeyondEOF, vs.edgecolour.allocated);
+ }
+ }
+ //Platform::DebugPrintf(
+ //"Layout:%9.6g Paint:%9.6g Ratio:%9.6g Copy:%9.6g Total:%9.6g\n",
+ //durLayout, durPaint, durLayout / durPaint, durCopy, etWhole.Duration());
+ NotifyPainted();
+ }
+}
+
+// Space (3 space characters) between line numbers and text when printing.
+#define lineNumberPrintSpace " "
+
+ColourDesired InvertedLight(ColourDesired orig) {
+ unsigned int r = orig.GetRed();
+ unsigned int g = orig.GetGreen();
+ unsigned int b = orig.GetBlue();
+ unsigned int l = (r + g + b) / 3; // There is a better calculation for this that matches human eye
+ unsigned int il = 0xff - l;
+ if (l == 0)
+ return ColourDesired(0xff, 0xff, 0xff);
+ r = r * il / l;
+ g = g * il / l;
+ b = b * il / l;
+ return ColourDesired(Platform::Minimum(r, 0xff), Platform::Minimum(g, 0xff), Platform::Minimum(b, 0xff));
+}
+
+// This is mostly copied from the Paint method but with some things omitted
+// such as the margin markers, line numbers, selection and caret
+// Should be merged back into a combined Draw method.
+long Editor::FormatRange(bool draw, RangeToFormat *pfr) {
+ if (!pfr)
+ return 0;
+
+ AutoSurface surface(pfr->hdc, this);
+ if (!surface)
+ return 0;
+ AutoSurface surfaceMeasure(pfr->hdcTarget, this);
+ if (!surfaceMeasure) {
+ return 0;
+ }
+
+ ViewStyle vsPrint(vs);
+
+ // Modify the view style for printing as do not normally want any of the transient features to be printed
+ // Printing supports only the line number margin.
+ int lineNumberIndex = -1;
+ for (int margin = 0; margin < ViewStyle::margins; margin++) {
+ if ((vsPrint.ms[margin].style == SC_MARGIN_NUMBER) && (vsPrint.ms[margin].width > 0)) {
+ lineNumberIndex = margin;
+ } else {
+ vsPrint.ms[margin].width = 0;
+ }
+ }
+ vsPrint.showMarkedLines = false;
+ vsPrint.fixedColumnWidth = 0;
+ vsPrint.zoomLevel = printMagnification;
+ vsPrint.viewIndentationGuides = false;
+ // Don't show the selection when printing
+ vsPrint.selbackset = false;
+ vsPrint.selforeset = false;
+ vsPrint.selAlpha = SC_ALPHA_NOALPHA;
+ vsPrint.whitespaceBackgroundSet = false;
+ vsPrint.whitespaceForegroundSet = false;
+ vsPrint.showCaretLineBackground = false;
+
+ // Set colours for printing according to users settings
+ for (int sty = 0;sty <= STYLE_MAX;sty++) {
+ if (printColourMode == SC_PRINT_INVERTLIGHT) {
+ vsPrint.styles[sty].fore.desired = InvertedLight(vsPrint.styles[sty].fore.desired);
+ vsPrint.styles[sty].back.desired = InvertedLight(vsPrint.styles[sty].back.desired);
+ } else if (printColourMode == SC_PRINT_BLACKONWHITE) {
+ vsPrint.styles[sty].fore.desired = ColourDesired(0, 0, 0);
+ vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
+ } else if (printColourMode == SC_PRINT_COLOURONWHITE) {
+ vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
+ } else if (printColourMode == SC_PRINT_COLOURONWHITEDEFAULTBG) {
+ if (sty <= STYLE_DEFAULT) {
+ vsPrint.styles[sty].back.desired = ColourDesired(0xff, 0xff, 0xff);
+ }
+ }
+ }
+ // White background for the line numbers
+ vsPrint.styles[STYLE_LINENUMBER].back.desired = ColourDesired(0xff, 0xff, 0xff);
+
+ vsPrint.Refresh(*surfaceMeasure);
+ // Ensure colours are set up
+ vsPrint.RefreshColourPalette(palette, true);
+ vsPrint.RefreshColourPalette(palette, false);
+ // Determining width must hapen after fonts have been realised in Refresh
+ int lineNumberWidth = 0;
+ if (lineNumberIndex >= 0) {
+ lineNumberWidth = surfaceMeasure->WidthText(vsPrint.styles[STYLE_LINENUMBER].font,
+ "99999" lineNumberPrintSpace, 5 + istrlen(lineNumberPrintSpace));
+ vsPrint.ms[lineNumberIndex].width = lineNumberWidth;
+ }
+
+ int linePrintStart = pdoc->LineFromPosition(pfr->chrg.cpMin);
+ int linePrintLast = linePrintStart + (pfr->rc.bottom - pfr->rc.top) / vsPrint.lineHeight - 1;
+ if (linePrintLast < linePrintStart)
+ linePrintLast = linePrintStart;
+ int linePrintMax = pdoc->LineFromPosition(pfr->chrg.cpMax);
+ if (linePrintLast > linePrintMax)
+ linePrintLast = linePrintMax;
+ //Platform::DebugPrintf("Formatting lines=[%0d,%0d,%0d] top=%0d bottom=%0d line=%0d %0d\n",
+ // linePrintStart, linePrintLast, linePrintMax, pfr->rc.top, pfr->rc.bottom, vsPrint.lineHeight,
+ // surfaceMeasure->Height(vsPrint.styles[STYLE_LINENUMBER].font));
+ int endPosPrint = pdoc->Length();
+ if (linePrintLast < pdoc->LinesTotal())
+ endPosPrint = pdoc->LineStart(linePrintLast + 1);
+
+ // Ensure we are styled to where we are formatting.
+ pdoc->EnsureStyledTo(endPosPrint);
+
+ int xStart = vsPrint.fixedColumnWidth + pfr->rc.left + lineNumberWidth;
+ int ypos = pfr->rc.top;
+
+ int lineDoc = linePrintStart;
+
+ int nPrintPos = pfr->chrg.cpMin;
+ int visibleLine = 0;
+ int widthPrint = pfr->rc.Width() - lineNumberWidth;
+ if (printWrapState == eWrapNone)
+ widthPrint = LineLayout::wrapWidthInfinite;
+
+ while (lineDoc <= linePrintLast && ypos < pfr->rc.bottom) {
+
+ // When printing, the hdc and hdcTarget may be the same, so
+ // changing the state of surfaceMeasure may change the underlying
+ // state of surface. Therefore, any cached state is discarded before
+ // using each surface.
+ surfaceMeasure->FlushCachedState();
+
+ // Copy this line and its styles from the document into local arrays
+ // and determine the x position at which each character starts.
+ LineLayout ll(8000);
+ LayoutLine(lineDoc, surfaceMeasure, vsPrint, &ll, widthPrint);
+
+ ll.selStart = -1;
+ ll.selEnd = -1;
+ ll.containsCaret = false;
+
+ PRectangle rcLine;
+ rcLine.left = pfr->rc.left + lineNumberWidth;
+ rcLine.top = ypos;
+ rcLine.right = pfr->rc.right - 1;
+ rcLine.bottom = ypos + vsPrint.lineHeight;
+
+ // When document line is wrapped over multiple display lines, find where
+ // to start printing from to ensure a particular position is on the first
+ // line of the page.
+ if (visibleLine == 0) {
+ int startWithinLine = nPrintPos - pdoc->LineStart(lineDoc);
+ for (int iwl = 0; iwl < ll.lines - 1; iwl++) {
+ if (ll.LineStart(iwl) <= startWithinLine && ll.LineStart(iwl + 1) >= startWithinLine) {
+ visibleLine = -iwl;
+ }
+ }
+
+ if (ll.lines > 1 && startWithinLine >= ll.LineStart(ll.lines - 1)) {
+ visibleLine = -(ll.lines - 1);
+ }
+ }
+
+ if (draw && lineNumberWidth &&
+ (ypos + vsPrint.lineHeight <= pfr->rc.bottom) &&
+ (visibleLine >= 0)) {
+ char number[100];
+ sprintf(number, "%d" lineNumberPrintSpace, lineDoc + 1);
+ PRectangle rcNumber = rcLine;
+ rcNumber.right = rcNumber.left + lineNumberWidth;
+ // Right justify
+ rcNumber.left -= surfaceMeasure->WidthText(
+ vsPrint.styles[STYLE_LINENUMBER].font, number, istrlen(number));
+ surface->FlushCachedState();
+ surface->DrawTextNoClip(rcNumber, vsPrint.styles[STYLE_LINENUMBER].font,
+ ypos + vsPrint.maxAscent, number, istrlen(number),
+ vsPrint.styles[STYLE_LINENUMBER].fore.allocated,
+ vsPrint.styles[STYLE_LINENUMBER].back.allocated);
+ }
+
+ // Draw the line
+ surface->FlushCachedState();
+
+ for (int iwl = 0; iwl < ll.lines; iwl++) {
+ if (ypos + vsPrint.lineHeight <= pfr->rc.bottom) {
+ if (visibleLine >= 0) {
+ if (draw) {
+ rcLine.top = ypos;
+ rcLine.bottom = ypos + vsPrint.lineHeight;
+ DrawLine(surface, vsPrint, lineDoc, visibleLine, xStart, rcLine, &ll, iwl);
+ }
+ ypos += vsPrint.lineHeight;
+ }
+ visibleLine++;
+ if (iwl == ll.lines - 1)
+ nPrintPos = pdoc->LineStart(lineDoc + 1);
+ else
+ nPrintPos += ll.LineStart(iwl + 1) - ll.LineStart(iwl);
+ }
+ }
+
+ ++lineDoc;
+ }
+
+ return nPrintPos;
+}
+
+int Editor::TextWidth(int style, const char *text) {
+ RefreshStyleData();
+ AutoSurface surface(this);
+ if (surface) {
+ return surface->WidthText(vs.styles[style].font, text, istrlen(text));
+ } else {
+ return 1;
+ }
+}
+
+// Empty method is overridden on GTK+ to show / hide scrollbars
+void Editor::ReconfigureScrollBars() {}
+
+void Editor::SetScrollBars() {
+ RefreshStyleData();
+
+ int nMax = MaxScrollPos();
+ int nPage = LinesOnScreen();
+ bool modified = ModifyScrollBars(nMax + nPage - 1, nPage);
+ if (modified) {
+ DwellEnd(true);
+ }
+
+ // TODO: ensure always showing as many lines as possible
+ // May not be, if, for example, window made larger
+ if (topLine > MaxScrollPos()) {
+ SetTopLine(Platform::Clamp(topLine, 0, MaxScrollPos()));
+ SetVerticalScrollPos();
+ Redraw();
+ }
+ if (modified) {
+ if (!AbandonPaint())
+ Redraw();
+ }
+ //Platform::DebugPrintf("end max = %d page = %d\n", nMax, nPage);
+}
+
+void Editor::ChangeSize() {
+ DropGraphics();
+ SetScrollBars();
+ if (wrapState != eWrapNone) {
+ PRectangle rcTextArea = GetClientRectangle();
+ rcTextArea.left = vs.fixedColumnWidth;
+ rcTextArea.right -= vs.rightMarginWidth;
+ if (wrapWidth != rcTextArea.Width()) {
+ NeedWrapping();
+ Redraw();
+ }
+ }
+}
+
+void Editor::AddChar(char ch) {
+ char s[2];
+ s[0] = ch;
+ s[1] = '\0';
+ AddCharUTF(s, 1);
+}
+
+void Editor::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
+ bool wasSelection = currentPos != anchor;
+ ClearSelection();
+ bool charReplaceAction = false;
+ if (inOverstrike && !wasSelection && !RangeContainsProtected(currentPos, currentPos + 1)) {
+ if (currentPos < (pdoc->Length())) {
+ if (!IsEOLChar(pdoc->CharAt(currentPos))) {
+ charReplaceAction = true;
+ pdoc->BeginUndoAction();
+ pdoc->DelChar(currentPos);
+ }
+ }
+ }
+ if (pdoc->InsertString(currentPos, s, len)) {
+ SetEmptySelection(currentPos + len);
+ }
+ if (charReplaceAction) {
+ pdoc->EndUndoAction();
+ }
+ EnsureCaretVisible();
+ // Avoid blinking during rapid typing:
+ ShowCaretAtCurrentPosition();
+ if (!caretSticky) {
+ SetLastXChosen();
+ }
+
+ if (treatAsDBCS) {
+ NotifyChar((static_cast<unsigned char>(s[0]) << 8) |
+ static_cast<unsigned char>(s[1]));
+ } else {
+ int byte = static_cast<unsigned char>(s[0]);
+ if ((byte < 0xC0) || (1 == len)) {
+ // Handles UTF-8 characters between 0x01 and 0x7F and single byte
+ // characters when not in UTF-8 mode.
+ // Also treats \0 and naked trail bytes 0x80 to 0xBF as valid
+ // characters representing themselves.
+ } else {
+ // Unroll 1 to 3 byte UTF-8 sequences. See reference data at:
+ // http://www.cl.cam.ac.uk/~mgk25/unicode.html
+ // http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
+ if (byte < 0xE0) {
+ int byte2 = static_cast<unsigned char>(s[1]);
+ if ((byte2 & 0xC0) == 0x80) {
+ // Two-byte-character lead-byte followed by a trail-byte.
+ byte = (((byte & 0x1F) << 6) | (byte2 & 0x3F));
+ }
+ // A two-byte-character lead-byte not followed by trail-byte
+ // represents itself.
+ } else if (byte < 0xF0) {
+ int byte2 = static_cast<unsigned char>(s[1]);
+ int byte3 = static_cast<unsigned char>(s[2]);
+ if (((byte2 & 0xC0) == 0x80) && ((byte3 & 0xC0) == 0x80)) {
+ // Three-byte-character lead byte followed by two trail bytes.
+ byte = (((byte & 0x0F) << 12) | ((byte2 & 0x3F) << 6) |
+ (byte3 & 0x3F));
+ }
+ // A three-byte-character lead-byte not followed by two trail-bytes
+ // represents itself.
+ }
+ }
+ NotifyChar(byte);
+ }
+}
+
+void Editor::ClearSelection() {
+ if (!SelectionContainsProtected()) {
+ int startPos = SelectionStart();
+ if (selType == selStream) {
+ unsigned int chars = SelectionEnd() - startPos;
+ if (0 != chars) {
+ pdoc->BeginUndoAction();
+ pdoc->DeleteChars(startPos, chars);
+ pdoc->EndUndoAction();
+ }
+ } else {
+ pdoc->BeginUndoAction();
+ SelectionLineIterator lineIterator(this, false);
+ while (lineIterator.Iterate()) {
+ startPos = lineIterator.startPos;
+ unsigned int chars = lineIterator.endPos - startPos;
+ if (0 != chars) {
+ pdoc->DeleteChars(startPos, chars);
+ }
+ }
+ pdoc->EndUndoAction();
+ selType = selStream;
+ }
+ SetEmptySelection(startPos);
+ }
+}
+
+void Editor::ClearAll() {
+ pdoc->BeginUndoAction();
+ if (0 != pdoc->Length()) {
+ pdoc->DeleteChars(0, pdoc->Length());
+ }
+ if (!pdoc->IsReadOnly()) {
+ cs.Clear();
+ }
+ pdoc->EndUndoAction();
+ anchor = 0;
+ currentPos = 0;
+ SetTopLine(0);
+ SetVerticalScrollPos();
+ InvalidateStyleRedraw();
+}
+
+void Editor::ClearDocumentStyle() {
+ pdoc->StartStyling(0, '\377');
+ pdoc->SetStyleFor(pdoc->Length(), 0);
+ cs.ShowAll();
+ pdoc->ClearLevels();
+}
+
+void Editor::Cut() {
+ if (!pdoc->IsReadOnly() && !SelectionContainsProtected()) {
+ Copy();
+ ClearSelection();
+ }
+}
+
+void Editor::PasteRectangular(int pos, const char *ptr, int len) {
+ if (pdoc->IsReadOnly() || SelectionContainsProtected()) {
+ return;
+ }
+ currentPos = pos;
+ int xInsert = XFromPosition(currentPos);
+ int line = pdoc->LineFromPosition(currentPos);
+ bool prevCr = false;
+ pdoc->BeginUndoAction();
+ for (int i = 0; i < len; i++) {
+ if (IsEOLChar(ptr[i])) {
+ if ((ptr[i] == '\r') || (!prevCr))
+ line++;
+ if (line >= pdoc->LinesTotal()) {
+ if (pdoc->eolMode != SC_EOL_LF)
+ pdoc->InsertChar(pdoc->Length(), '\r');
+ if (pdoc->eolMode != SC_EOL_CR)
+ pdoc->InsertChar(pdoc->Length(), '\n');
+ }
+ // Pad the end of lines with spaces if required
+ currentPos = PositionFromLineX(line, xInsert);
+ if ((XFromPosition(currentPos) < xInsert) && (i + 1 < len)) {
+ for (int i = 0; i < xInsert - XFromPosition(currentPos); i++) {
+ pdoc->InsertChar(currentPos, ' ');
+ currentPos++;
+ }
+ }
+ prevCr = ptr[i] == '\r';
+ } else {
+ pdoc->InsertString(currentPos, ptr + i, 1);
+ currentPos++;
+ prevCr = false;
+ }
+ }
+ pdoc->EndUndoAction();
+ SetEmptySelection(pos);
+}
+
+bool Editor::CanPaste() {
+ return !pdoc->IsReadOnly() && !SelectionContainsProtected();
+}
+
+void Editor::Clear() {
+ if (currentPos == anchor) {
+ if (!RangeContainsProtected(currentPos, currentPos + 1)) {
+ DelChar();
+ }
+ } else {
+ ClearSelection();
+ }
+ SetEmptySelection(currentPos);
+}
+
+void Editor::SelectAll() {
+ SetSelection(0, pdoc->Length());
+ Redraw();
+}
+
+void Editor::Undo() {
+ if (pdoc->CanUndo()) {
+ InvalidateCaret();
+ int newPos = pdoc->Undo();
+ if (newPos >= 0)
+ SetEmptySelection(newPos);
+ EnsureCaretVisible();
+ }
+}
+
+void Editor::Redo() {
+ if (pdoc->CanRedo()) {
+ int newPos = pdoc->Redo();
+ if (newPos >= 0)
+ SetEmptySelection(newPos);
+ EnsureCaretVisible();
+ }
+}
+
+void Editor::DelChar() {
+ if (!RangeContainsProtected(currentPos, currentPos + 1)) {
+ pdoc->DelChar(currentPos);
+ }
+ // Avoid blinking during rapid typing:
+ ShowCaretAtCurrentPosition();
+}
+
+void Editor::DelCharBack(bool allowLineStartDeletion) {
+ if (currentPos == anchor) {
+ if (!RangeContainsProtected(currentPos - 1, currentPos)) {
+ int lineCurrentPos = pdoc->LineFromPosition(currentPos);
+ if (allowLineStartDeletion || (pdoc->LineStart(lineCurrentPos) != currentPos)) {
+ if (pdoc->GetColumn(currentPos) <= pdoc->GetLineIndentation(lineCurrentPos) &&
+ pdoc->GetColumn(currentPos) > 0 && pdoc->backspaceUnindents) {
+ pdoc->BeginUndoAction();
+ int indentation = pdoc->GetLineIndentation(lineCurrentPos);
+ int indentationStep = pdoc->IndentSize();
+ if (indentation % indentationStep == 0) {
+ pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
+ } else {
+ pdoc->SetLineIndentation(lineCurrentPos, indentation - (indentation % indentationStep));
+ }
+ SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos));
+ pdoc->EndUndoAction();
+ } else {
+ pdoc->DelCharBack(currentPos);
+ }
+ }
+ }
+ } else {
+ ClearSelection();
+ SetEmptySelection(currentPos);
+ }
+ // Avoid blinking during rapid typing:
+ ShowCaretAtCurrentPosition();
+}
+
+void Editor::NotifyFocus(bool) {}
+
+void Editor::NotifyStyleToNeeded(int endStyleNeeded) {
+ SCNotification scn = {0};
+ scn.nmhdr.code = SCN_STYLENEEDED;
+ scn.position = endStyleNeeded;
+ NotifyParent(scn);
+}
+
+void Editor::NotifyStyleNeeded(Document*, void *, int endStyleNeeded) {
+ NotifyStyleToNeeded(endStyleNeeded);
+}
+
+void Editor::NotifyChar(int ch) {
+ SCNotification scn = {0};
+ scn.nmhdr.code = SCN_CHARADDED;
+ scn.ch = ch;
+ NotifyParent(scn);
+ if (recordingMacro) {
+ char txt[2];
+ txt[0] = static_cast<char>(ch);
+ txt[1] = '\0';
+ NotifyMacroRecord(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(txt));
+ }
+}
+
+void Editor::NotifySavePoint(bool isSavePoint) {
+ SCNotification scn = {0};
+ if (isSavePoint) {
+ scn.nmhdr.code = SCN_SAVEPOINTREACHED;
+ } else {
+ scn.nmhdr.code = SCN_SAVEPOINTLEFT;
+ }
+ NotifyParent(scn);
+}
+
+void Editor::NotifyModifyAttempt() {
+ SCNotification scn = {0};
+ scn.nmhdr.code = SCN_MODIFYATTEMPTRO;
+ NotifyParent(scn);
+}
+
+void Editor::NotifyDoubleClick(Point pt, bool) {
+ SCNotification scn = {0};
+ scn.nmhdr.code = SCN_DOUBLECLICK;
+ scn.line = LineFromLocation(pt);
+ scn.position = PositionFromLocationClose(pt);
+ NotifyParent(scn);
+}
+
+void Editor::NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt) {
+ SCNotification scn = {0};
+ scn.nmhdr.code = SCN_HOTSPOTDOUBLECLICK;
+ scn.position = position;
+ scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
+ (alt ? SCI_ALT : 0);
+ NotifyParent(scn);
+}
+
+void Editor::NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt) {
+ SCNotification scn = {0};
+ scn.nmhdr.code = SCN_HOTSPOTCLICK;
+ scn.position = position;
+ scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
+ (alt ? SCI_ALT : 0);
+ NotifyParent(scn);
+}
+
+void Editor::NotifyUpdateUI() {
+ SCNotification scn = {0};
+ scn.nmhdr.code = SCN_UPDATEUI;
+ NotifyParent(scn);
+}
+
+void Editor::NotifyPainted() {
+ SCNotification scn = {0};
+ scn.nmhdr.code = SCN_PAINTED;
+ NotifyParent(scn);
+}
+
+bool Editor::NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt) {
+ int marginClicked = -1;
+ int x = 0;
+ for (int margin = 0; margin < ViewStyle::margins; margin++) {
+ if ((pt.x > x) && (pt.x < x + vs.ms[margin].width))
+ marginClicked = margin;
+ x += vs.ms[margin].width;
+ }
+ if ((marginClicked >= 0) && vs.ms[marginClicked].sensitive) {
+ SCNotification scn = {0};
+ scn.nmhdr.code = SCN_MARGINCLICK;
+ scn.modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
+ (alt ? SCI_ALT : 0);
+ scn.position = pdoc->LineStart(LineFromLocation(pt));
+ scn.margin = marginClicked;
+ NotifyParent(scn);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void Editor::NotifyNeedShown(int pos, int len) {
+ SCNotification scn = {0};
+ scn.nmhdr.code = SCN_NEEDSHOWN;
+ scn.position = pos;
+ scn.length = len;
+ NotifyParent(scn);
+}
+
+void Editor::NotifyDwelling(Point pt, bool state) {
+ SCNotification scn = {0};
+ scn.nmhdr.code = state ? SCN_DWELLSTART : SCN_DWELLEND;
+ scn.position = PositionFromLocationClose(pt);
+ scn.x = pt.x;
+ scn.y = pt.y;
+ NotifyParent(scn);
+}
+
+void Editor::NotifyZoom() {
+ SCNotification scn = {0};
+ scn.nmhdr.code = SCN_ZOOM;
+ NotifyParent(scn);
+}
+
+// Notifications from document
+void Editor::NotifyModifyAttempt(Document*, void *) {
+ //Platform::DebugPrintf("** Modify Attempt\n");
+ NotifyModifyAttempt();
+}
+
+void Editor::NotifyMove(int position) {
+#ifdef INCLUDE_DEPRECATED_FEATURES
+ SCNotification scn = {0};
+ scn.nmhdr.code = SCN_POSCHANGED;
+ scn.position = position;
+ NotifyParent(scn);
+#endif
+}
+
+void Editor::NotifySavePoint(Document*, void *, bool atSavePoint) {
+ //Platform::DebugPrintf("** Save Point %s\n", atSavePoint ? "On" : "Off");
+ NotifySavePoint(atSavePoint);
+}
+
+void Editor::CheckModificationForWrap(DocModification mh) {
+ if (mh.modificationType & (SC_MOD_INSERTTEXT|SC_MOD_DELETETEXT)) {
+ llc.Invalidate(LineLayout::llCheckTextAndStyle);
+ if (wrapState != eWrapNone) {
+ int lineDoc = pdoc->LineFromPosition(mh.position);
+ int lines = Platform::Maximum(0, mh.linesAdded);
+ NeedWrapping(lineDoc, lineDoc + lines + 1);
+ }
+ }
+}
+
+// Move a position so it is still after the same character as before the insertion.
+static inline int MovePositionForInsertion(int position, int startInsertion, int length) {
+ if (position > startInsertion) {
+ return position + length;
+ }
+ return position;
+}
+
+// Move a position so it is still after the same character as before the deletion if that
+// character is still present else after the previous surviving character.
+static inline int MovePositionForDeletion(int position, int startDeletion, int length) {
+ if (position > startDeletion) {
+ int endDeletion = startDeletion + length;
+ if (position > endDeletion) {
+ return position - length;
+ } else {
+ return startDeletion;
+ }
+ } else {
+ return position;
+ }
+}
+
+void Editor::NotifyModified(Document*, DocModification mh, void *) {
+ needUpdateUI = true;
+ if (paintState == painting) {
+ CheckForChangeOutsidePaint(Range(mh.position, mh.position + mh.length));
+ }
+ if (mh.modificationType & SC_MOD_CHANGESTYLE) {
+ pdoc->IncrementStyleClock();
+ if (paintState == notPainting) {
+ if (mh.position < pdoc->LineStart(topLine)) {
+ // Styling performed before this view
+ Redraw();
+ } else {
+ InvalidateRange(mh.position, mh.position + mh.length);
+ }
+ }
+ llc.Invalidate(LineLayout::llCheckTextAndStyle);
+ } else {
+ // Move selection and brace highlights
+ if (mh.modificationType & SC_MOD_INSERTTEXT) {
+ currentPos = MovePositionForInsertion(currentPos, mh.position, mh.length);
+ anchor = MovePositionForInsertion(anchor, mh.position, mh.length);
+ braces[0] = MovePositionForInsertion(braces[0], mh.position, mh.length);
+ braces[1] = MovePositionForInsertion(braces[1], mh.position, mh.length);
+ } else if (mh.modificationType & SC_MOD_DELETETEXT) {
+ currentPos = MovePositionForDeletion(currentPos, mh.position, mh.length);
+ anchor = MovePositionForDeletion(anchor, mh.position, mh.length);
+ braces[0] = MovePositionForDeletion(braces[0], mh.position, mh.length);
+ braces[1] = MovePositionForDeletion(braces[1], mh.position, mh.length);
+ }
+ if (cs.LinesDisplayed() < cs.LinesInDoc()) {
+ // Some lines are hidden so may need shown.
+ // TODO: check if the modified area is hidden.
+ if (mh.modificationType & SC_MOD_BEFOREINSERT) {
+ NotifyNeedShown(mh.position, 0);
+ } else if (mh.modificationType & SC_MOD_BEFOREDELETE) {
+ NotifyNeedShown(mh.position, mh.length);
+ }
+ }
+ if (mh.linesAdded != 0) {
+ // Update contraction state for inserted and removed lines
+ // lineOfPos should be calculated in context of state before modification, shouldn't it
+ int lineOfPos = pdoc->LineFromPosition(mh.position);
+ if (mh.linesAdded > 0) {
+ cs.InsertLines(lineOfPos, mh.linesAdded);
+ } else {
+ cs.DeleteLines(lineOfPos, -mh.linesAdded);
+ }
+ }
+ CheckModificationForWrap(mh);
+ if (mh.linesAdded != 0) {
+ // Avoid scrolling of display if change before current display
+ if (mh.position < posTopLine && !CanDeferToLastStep(mh)) {
+ int newTop = Platform::Clamp(topLine + mh.linesAdded, 0, MaxScrollPos());
+ if (newTop != topLine) {
+ SetTopLine(newTop);
+ SetVerticalScrollPos();
+ }
+ }
+
+ //Platform::DebugPrintf("** %x Doc Changed\n", this);
+ // TODO: could invalidate from mh.startModification to end of screen
+ //InvalidateRange(mh.position, mh.position + mh.length);
+ if (paintState == notPainting && !CanDeferToLastStep(mh)) {
+ Redraw();
+ }
+ } else {
+ //Platform::DebugPrintf("** %x Line Changed %d .. %d\n", this,
+ // mh.position, mh.position + mh.length);
+ if (paintState == notPainting && mh.length && !CanEliminate(mh)) {
+ InvalidateRange(mh.position, mh.position + mh.length);
+ }
+ }
+ }
+
+ if (mh.linesAdded != 0 && !CanDeferToLastStep(mh)) {
+ SetScrollBars();
+ }
+
+ if (mh.modificationType & SC_MOD_CHANGEMARKER) {
+ if ((paintState == notPainting) || !PaintContainsMargin()) {
+ if (mh.modificationType & SC_MOD_CHANGEFOLD) {
+ // Fold changes can affect the drawing of following lines so redraw whole margin
+ RedrawSelMargin();
+ } else {
+ RedrawSelMargin(mh.line);
+ }
+ }
+ }
+
+ // NOW pay the piper WRT "deferred" visual updates
+ if (IsLastStep(mh)) {
+ SetScrollBars();
+ Redraw();
+ }
+
+ // If client wants to see this modification
+ if (mh.modificationType & modEventMask) {
+ if ((mh.modificationType & SC_MOD_CHANGESTYLE) == 0) {
+ // Real modification made to text of document.
+ NotifyChange(); // Send EN_CHANGE
+ }
+
+ SCNotification scn = {0};
+ scn.nmhdr.code = SCN_MODIFIED;
+ scn.position = mh.position;
+ scn.modificationType = mh.modificationType;
+ scn.text = mh.text;
+ scn.length = mh.length;
+ scn.linesAdded = mh.linesAdded;
+ scn.line = mh.line;
+ scn.foldLevelNow = mh.foldLevelNow;
+ scn.foldLevelPrev = mh.foldLevelPrev;
+ NotifyParent(scn);
+ }
+}
+
+void Editor::NotifyDeleted(Document *, void *) {
+ /* Do nothing */
+}
+
+void Editor::NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
+
+ // Enumerates all macroable messages
+ switch (iMessage) {
+ case SCI_CUT:
+ case SCI_COPY:
+ case SCI_PASTE:
+ case SCI_CLEAR:
+ case SCI_REPLACESEL:
+ case SCI_ADDTEXT:
+ case SCI_INSERTTEXT:
+ case SCI_APPENDTEXT:
+ case SCI_CLEARALL:
+ case SCI_SELECTALL:
+ case SCI_GOTOLINE:
+ case SCI_GOTOPOS:
+ case SCI_SEARCHANCHOR:
+ case SCI_SEARCHNEXT:
+ case SCI_SEARCHPREV:
+ case SCI_LINEDOWN:
+ case SCI_LINEDOWNEXTEND:
+ case SCI_PARADOWN:
+ case SCI_PARADOWNEXTEND:
+ case SCI_LINEUP:
+ case SCI_LINEUPEXTEND:
+ case SCI_PARAUP:
+ case SCI_PARAUPEXTEND:
+ case SCI_CHARLEFT:
+ case SCI_CHARLEFTEXTEND:
+ case SCI_CHARRIGHT:
+ case SCI_CHARRIGHTEXTEND:
+ case SCI_WORDLEFT:
+ case SCI_WORDLEFTEXTEND:
+ case SCI_WORDRIGHT:
+ case SCI_WORDRIGHTEXTEND:
+ case SCI_WORDPARTLEFT:
+ case SCI_WORDPARTLEFTEXTEND:
+ case SCI_WORDPARTRIGHT:
+ case SCI_WORDPARTRIGHTEXTEND:
+ case SCI_WORDLEFTEND:
+ case SCI_WORDLEFTENDEXTEND:
+ case SCI_WORDRIGHTEND:
+ case SCI_WORDRIGHTENDEXTEND:
+ case SCI_HOME:
+ case SCI_HOMEEXTEND:
+ case SCI_LINEEND:
+ case SCI_LINEENDEXTEND:
+ case SCI_HOMEWRAP:
+ case SCI_HOMEWRAPEXTEND:
+ case SCI_LINEENDWRAP:
+ case SCI_LINEENDWRAPEXTEND:
+ case SCI_DOCUMENTSTART:
+ case SCI_DOCUMENTSTARTEXTEND:
+ case SCI_DOCUMENTEND:
+ case SCI_DOCUMENTENDEXTEND:
+ case SCI_STUTTEREDPAGEUP:
+ case SCI_STUTTEREDPAGEUPEXTEND:
+ case SCI_STUTTEREDPAGEDOWN:
+ case SCI_STUTTEREDPAGEDOWNEXTEND:
+ case SCI_PAGEUP:
+ case SCI_PAGEUPEXTEND:
+ case SCI_PAGEDOWN:
+ case SCI_PAGEDOWNEXTEND:
+ case SCI_EDITTOGGLEOVERTYPE:
+ case SCI_CANCEL:
+ case SCI_DELETEBACK:
+ case SCI_TAB:
+ case SCI_BACKTAB:
+ case SCI_FORMFEED:
+ case SCI_VCHOME:
+ case SCI_VCHOMEEXTEND:
+ case SCI_VCHOMEWRAP:
+ case SCI_VCHOMEWRAPEXTEND:
+ case SCI_DELWORDLEFT:
+ case SCI_DELWORDRIGHT:
+ case SCI_DELLINELEFT:
+ case SCI_DELLINERIGHT:
+ case SCI_LINECOPY:
+ case SCI_LINECUT:
+ case SCI_LINEDELETE:
+ case SCI_LINETRANSPOSE:
+ case SCI_LINEDUPLICATE:
+ case SCI_LOWERCASE:
+ case SCI_UPPERCASE:
+ case SCI_LINESCROLLDOWN:
+ case SCI_LINESCROLLUP:
+ case SCI_DELETEBACKNOTLINE:
+ case SCI_HOMEDISPLAY:
+ case SCI_HOMEDISPLAYEXTEND:
+ case SCI_LINEENDDISPLAY:
+ case SCI_LINEENDDISPLAYEXTEND:
+ case SCI_SETSELECTIONMODE:
+ case SCI_LINEDOWNRECTEXTEND:
+ case SCI_LINEUPRECTEXTEND:
+ case SCI_CHARLEFTRECTEXTEND:
+ case SCI_CHARRIGHTRECTEXTEND:
+ case SCI_HOMERECTEXTEND:
+ case SCI_VCHOMERECTEXTEND:
+ case SCI_LINEENDRECTEXTEND:
+ case SCI_PAGEUPRECTEXTEND:
+ case SCI_PAGEDOWNRECTEXTEND:
+ case SCI_SELECTIONDUPLICATE:
+ break;
+
+ // Filter out all others like display changes. Also, newlines are redundant
+ // with char insert messages.
+ case SCI_NEWLINE:
+ default:
+ // printf("Filtered out %ld of macro recording\n", iMessage);
+ return ;
+ }
+
+ // Send notification
+ SCNotification scn = {0};
+ scn.nmhdr.code = SCN_MACRORECORD;
+ scn.message = iMessage;
+ scn.wParam = wParam;
+ scn.lParam = lParam;
+ NotifyParent(scn);
+}
+
+/**
+ * Force scroll and keep position relative to top of window.
+ *
+ * If stuttered = true and not already at first/last row, move to first/last row of window.
+ * If stuttered = true and already at first/last row, scroll as normal.
+ */
+void Editor::PageMove(int direction, selTypes sel, bool stuttered) {
+ int topLineNew, newPos;
+
+ // I consider only the caretYSlop, and ignore the caretYPolicy-- is that a problem?
+ int currentLine = pdoc->LineFromPosition(currentPos);
+ int topStutterLine = topLine + caretYSlop;
+ int bottomStutterLine = topLine + LinesToScroll() - caretYSlop;
+
+ if (stuttered && (direction < 0 && currentLine > topStutterLine)) {
+ topLineNew = topLine;
+ newPos = PositionFromLocation(Point(lastXChosen, vs.lineHeight * caretYSlop));
+
+ } else if (stuttered && (direction > 0 && currentLine < bottomStutterLine)) {
+ topLineNew = topLine;
+ newPos = PositionFromLocation(Point(lastXChosen, vs.lineHeight * (LinesToScroll() - caretYSlop)));
+
+ } else {
+ Point pt = LocationFromPosition(currentPos);
+
+ topLineNew = Platform::Clamp(
+ topLine + direction * LinesToScroll(), 0, MaxScrollPos());
+ newPos = PositionFromLocation(
+ Point(lastXChosen, pt.y + direction * (vs.lineHeight * LinesToScroll())));
+ }
+
+ if (topLineNew != topLine) {
+ SetTopLine(topLineNew);
+ MovePositionTo(newPos, sel);
+ Redraw();
+ SetVerticalScrollPos();
+ } else {
+ MovePositionTo(newPos, sel);
+ }
+}
+
+void Editor::ChangeCaseOfSelection(bool makeUpperCase) {
+ pdoc->BeginUndoAction();
+ int startCurrent = currentPos;
+ int startAnchor = anchor;
+ if (selType == selStream) {
+ pdoc->ChangeCase(Range(SelectionStart(), SelectionEnd()),
+ makeUpperCase);
+ SetSelection(startCurrent, startAnchor);
+ } else {
+ SelectionLineIterator lineIterator(this, false);
+ while (lineIterator.Iterate()) {
+ pdoc->ChangeCase(
+ Range(lineIterator.startPos, lineIterator.endPos),
+ makeUpperCase);
+ }
+ // Would be nicer to keep the rectangular selection but this is complex
+ SetEmptySelection(startCurrent);
+ }
+ pdoc->EndUndoAction();
+}
+
+void Editor::LineTranspose() {
+ int line = pdoc->LineFromPosition(currentPos);
+ if (line > 0) {
+ int startPrev = pdoc->LineStart(line - 1);
+ int endPrev = pdoc->LineEnd(line - 1);
+ int start = pdoc->LineStart(line);
+ int end = pdoc->LineEnd(line);
+ int startNext = pdoc->LineStart(line + 1);
+ if (end < pdoc->Length()) {
+ end = startNext;
+ char *thisLine = CopyRange(start, end);
+ pdoc->DeleteChars(start, end - start);
+ if (pdoc->InsertString(startPrev, thisLine, end - start)) {
+ MovePositionTo(startPrev + end - start);
+ }
+ delete []thisLine;
+ } else {
+ // Last line so line has no line end
+ char *thisLine = CopyRange(start, end);
+ char *prevEnd = CopyRange(endPrev, start);
+ pdoc->DeleteChars(endPrev, end - endPrev);
+ pdoc->InsertString(startPrev, thisLine, end - start);
+ if (pdoc->InsertString(startPrev + end - start, prevEnd, start - endPrev)) {
+ MovePositionTo(startPrev + end - endPrev);
+ }
+ delete []thisLine;
+ delete []prevEnd;
+ }
+
+ }
+}
+
+void Editor::Duplicate(bool forLine) {
+ int start = SelectionStart();
+ int end = SelectionEnd();
+ if (start == end) {
+ forLine = true;
+ }
+ if (forLine) {
+ int line = pdoc->LineFromPosition(currentPos);
+ start = pdoc->LineStart(line);
+ end = pdoc->LineEnd(line);
+ }
+ char *text = CopyRange(start, end);
+ if (forLine) {
+ const char *eol = StringFromEOLMode(pdoc->eolMode);
+ pdoc->InsertString(end, eol);
+ pdoc->InsertString(end + istrlen(eol), text, end - start);
+ } else {
+ pdoc->InsertString(end, text, end - start);
+ }
+ delete []text;
+}
+
+void Editor::CancelModes() {
+ moveExtendsSelection = false;
+}
+
+void Editor::NewLine() {
+ ClearSelection();
+ const char *eol = "\n";
+ if (pdoc->eolMode == SC_EOL_CRLF) {
+ eol = "\r\n";
+ } else if (pdoc->eolMode == SC_EOL_CR) {
+ eol = "\r";
+ } // else SC_EOL_LF -> "\n" already set
+ if (pdoc->InsertString(currentPos, eol)) {
+ SetEmptySelection(currentPos + istrlen(eol));
+ while (*eol) {
+ NotifyChar(*eol);
+ eol++;
+ }
+ }
+ SetLastXChosen();
+ EnsureCaretVisible();
+ // Avoid blinking during rapid typing:
+ ShowCaretAtCurrentPosition();
+}
+
+void Editor::CursorUpOrDown(int direction, selTypes sel) {
+ Point pt = LocationFromPosition(currentPos);
+ int posNew = PositionFromLocation(
+ Point(lastXChosen, pt.y + direction * vs.lineHeight));
+ if (direction < 0) {
+ // Line wrapping may lead to a location on the same line, so
+ // seek back if that is the case.
+ // There is an equivalent case when moving down which skips
+ // over a line but as that does not trap the user it is fine.
+ Point ptNew = LocationFromPosition(posNew);
+ while ((posNew > 0) && (pt.y == ptNew.y)) {
+ posNew--;
+ ptNew = LocationFromPosition(posNew);
+ }
+ }
+ MovePositionTo(posNew, sel);
+}
+
+void Editor::ParaUpOrDown(int direction, selTypes sel) {
+ int lineDoc, savedPos = currentPos;
+ do {
+ MovePositionTo(direction > 0 ? pdoc->ParaDown(currentPos) : pdoc->ParaUp(currentPos), sel);
+ lineDoc = pdoc->LineFromPosition(currentPos);
+ if (direction > 0) {
+ if (currentPos >= pdoc->Length() && !cs.GetVisible(lineDoc)) {
+ if (sel == noSel) {
+ MovePositionTo(pdoc->LineEndPosition(savedPos));
+ }
+ break;
+ }
+ }
+ } while (!cs.GetVisible(lineDoc));
+}
+
+int Editor::StartEndDisplayLine(int pos, bool start) {
+ RefreshStyleData();
+ int line = pdoc->LineFromPosition(pos);
+ AutoSurface surface(this);
+ AutoLineLayout ll(llc, RetrieveLineLayout(line));
+ int posRet = INVALID_POSITION;
+ if (surface && ll) {
+ unsigned int posLineStart = pdoc->LineStart(line);
+ LayoutLine(line, surface, vs, ll, wrapWidth);
+ int posInLine = pos - posLineStart;
+ if (posInLine <= ll->maxLineLength) {
+ for (int subLine = 0; subLine < ll->lines; subLine++) {
+ if ((posInLine >= ll->LineStart(subLine)) && (posInLine <= ll->LineStart(subLine + 1))) {
+ if (start) {
+ posRet = ll->LineStart(subLine) + posLineStart;
+ } else {
+ if (subLine == ll->lines - 1)
+ posRet = ll->LineStart(subLine + 1) + posLineStart;
+ else
+ posRet = ll->LineStart(subLine + 1) + posLineStart - 1;
+ }
+ }
+ }
+ }
+ }
+ if (posRet == INVALID_POSITION) {
+ return pos;
+ } else {
+ return posRet;
+ }
+}
+
+int Editor::KeyCommand(unsigned int iMessage) {
+ switch (iMessage) {
+ case SCI_LINEDOWN:
+ CursorUpOrDown(1);
+ break;
+ case SCI_LINEDOWNEXTEND:
+ CursorUpOrDown(1, selStream);
+ break;
+ case SCI_LINEDOWNRECTEXTEND:
+ CursorUpOrDown(1, selRectangle);
+ break;
+ case SCI_PARADOWN:
+ ParaUpOrDown(1);
+ break;
+ case SCI_PARADOWNEXTEND:
+ ParaUpOrDown(1, selStream);
+ break;
+ case SCI_LINESCROLLDOWN:
+ ScrollTo(topLine + 1);
+ MoveCaretInsideView(false);
+ break;
+ case SCI_LINEUP:
+ CursorUpOrDown(-1);
+ break;
+ case SCI_LINEUPEXTEND:
+ CursorUpOrDown(-1, selStream);
+ break;
+ case SCI_LINEUPRECTEXTEND:
+ CursorUpOrDown(-1, selRectangle);
+ break;
+ case SCI_PARAUP:
+ ParaUpOrDown(-1);
+ break;
+ case SCI_PARAUPEXTEND:
+ ParaUpOrDown(-1, selStream);
+ break;
+ case SCI_LINESCROLLUP:
+ ScrollTo(topLine - 1);
+ MoveCaretInsideView(false);
+ break;
+ case SCI_CHARLEFT:
+ if (SelectionEmpty() || moveExtendsSelection) {
+ MovePositionTo(MovePositionSoVisible(currentPos - 1, -1));
+ } else {
+ MovePositionTo(SelectionStart());
+ }
+ SetLastXChosen();
+ break;
+ case SCI_CHARLEFTEXTEND:
+ MovePositionTo(MovePositionSoVisible(currentPos - 1, -1), selStream);
+ SetLastXChosen();
+ break;
+ case SCI_CHARLEFTRECTEXTEND:
+ MovePositionTo(MovePositionSoVisible(currentPos - 1, -1), selRectangle);
+ SetLastXChosen();
+ break;
+ case SCI_CHARRIGHT:
+ if (SelectionEmpty() || moveExtendsSelection) {
+ MovePositionTo(MovePositionSoVisible(currentPos + 1, 1));
+ } else {
+ MovePositionTo(SelectionEnd());
+ }
+ SetLastXChosen();
+ break;
+ case SCI_CHARRIGHTEXTEND:
+ MovePositionTo(MovePositionSoVisible(currentPos + 1, 1), selStream);
+ SetLastXChosen();
+ break;
+ case SCI_CHARRIGHTRECTEXTEND:
+ MovePositionTo(MovePositionSoVisible(currentPos + 1, 1), selRectangle);
+ SetLastXChosen();
+ break;
+ case SCI_WORDLEFT:
+ MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1));
+ SetLastXChosen();
+ break;
+ case SCI_WORDLEFTEXTEND:
+ MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, -1), -1), selStream);
+ SetLastXChosen();
+ break;
+ case SCI_WORDRIGHT:
+ MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1));
+ SetLastXChosen();
+ break;
+ case SCI_WORDRIGHTEXTEND:
+ MovePositionTo(MovePositionSoVisible(pdoc->NextWordStart(currentPos, 1), 1), selStream);
+ SetLastXChosen();
+ break;
+
+ case SCI_WORDLEFTEND:
+ MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, -1), -1));
+ SetLastXChosen();
+ break;
+ case SCI_WORDLEFTENDEXTEND:
+ MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, -1), -1), selStream);
+ SetLastXChosen();
+ break;
+ case SCI_WORDRIGHTEND:
+ MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, 1), 1));
+ SetLastXChosen();
+ break;
+ case SCI_WORDRIGHTENDEXTEND:
+ MovePositionTo(MovePositionSoVisible(pdoc->NextWordEnd(currentPos, 1), 1), selStream);
+ SetLastXChosen();
+ break;
+
+ case SCI_HOME:
+ MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)));
+ SetLastXChosen();
+ break;
+ case SCI_HOMEEXTEND:
+ MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)), selStream);
+ SetLastXChosen();
+ break;
+ case SCI_HOMERECTEXTEND:
+ MovePositionTo(pdoc->LineStart(pdoc->LineFromPosition(currentPos)), selRectangle);
+ SetLastXChosen();
+ break;
+ case SCI_LINEEND:
+ MovePositionTo(pdoc->LineEndPosition(currentPos));
+ SetLastXChosen();
+ break;
+ case SCI_LINEENDEXTEND:
+ MovePositionTo(pdoc->LineEndPosition(currentPos), selStream);
+ SetLastXChosen();
+ break;
+ case SCI_LINEENDRECTEXTEND:
+ MovePositionTo(pdoc->LineEndPosition(currentPos), selRectangle);
+ SetLastXChosen();
+ break;
+ case SCI_HOMEWRAP: {
+ int homePos = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1);
+ if (currentPos <= homePos)
+ homePos = pdoc->LineStart(pdoc->LineFromPosition(currentPos));
+ MovePositionTo(homePos);
+ SetLastXChosen();
+ }
+ break;
+ case SCI_HOMEWRAPEXTEND: {
+ int homePos = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1);
+ if (currentPos <= homePos)
+ homePos = pdoc->LineStart(pdoc->LineFromPosition(currentPos));
+ MovePositionTo(homePos, selStream);
+ SetLastXChosen();
+ }
+ break;
+ case SCI_LINEENDWRAP: {
+ int endPos = MovePositionSoVisible(StartEndDisplayLine(currentPos, false), 1);
+ int realEndPos = pdoc->LineEndPosition(currentPos);
+ if (endPos > realEndPos // if moved past visible EOLs
+ || currentPos >= endPos) // if at end of display line already
+ endPos = realEndPos;
+ MovePositionTo(endPos);
+ SetLastXChosen();
+ }
+ break;
+ case SCI_LINEENDWRAPEXTEND: {
+ int endPos = MovePositionSoVisible(StartEndDisplayLine(currentPos, false), 1);
+ int realEndPos = pdoc->LineEndPosition(currentPos);
+ if (endPos > realEndPos // if moved past visible EOLs
+ || currentPos >= endPos) // if at end of display line already
+ endPos = realEndPos;
+ MovePositionTo(endPos, selStream);
+ SetLastXChosen();
+ }
+ break;
+ case SCI_DOCUMENTSTART:
+ MovePositionTo(0);
+ SetLastXChosen();
+ break;
+ case SCI_DOCUMENTSTARTEXTEND:
+ MovePositionTo(0, selStream);
+ SetLastXChosen();
+ break;
+ case SCI_DOCUMENTEND:
+ MovePositionTo(pdoc->Length());
+ SetLastXChosen();
+ break;
+ case SCI_DOCUMENTENDEXTEND:
+ MovePositionTo(pdoc->Length(), selStream);
+ SetLastXChosen();
+ break;
+ case SCI_STUTTEREDPAGEUP:
+ PageMove(-1, noSel, true);
+ break;
+ case SCI_STUTTEREDPAGEUPEXTEND:
+ PageMove(-1, selStream, true);
+ break;
+ case SCI_STUTTEREDPAGEDOWN:
+ PageMove(1, noSel, true);
+ break;
+ case SCI_STUTTEREDPAGEDOWNEXTEND:
+ PageMove(1, selStream, true);
+ break;
+ case SCI_PAGEUP:
+ PageMove(-1);
+ break;
+ case SCI_PAGEUPEXTEND:
+ PageMove(-1, selStream);
+ break;
+ case SCI_PAGEUPRECTEXTEND:
+ PageMove(-1, selRectangle);
+ break;
+ case SCI_PAGEDOWN:
+ PageMove(1);
+ break;
+ case SCI_PAGEDOWNEXTEND:
+ PageMove(1, selStream);
+ break;
+ case SCI_PAGEDOWNRECTEXTEND:
+ PageMove(1, selRectangle);
+ break;
+ case SCI_EDITTOGGLEOVERTYPE:
+ inOverstrike = !inOverstrike;
+ DropCaret();
+ ShowCaretAtCurrentPosition();
+ NotifyUpdateUI();
+ break;
+ case SCI_CANCEL: // Cancel any modes - handled in subclass
+ // Also unselect text
+ CancelModes();
+ break;
+ case SCI_DELETEBACK:
+ DelCharBack(true);
+ if (!caretSticky) {
+ SetLastXChosen();
+ }
+ EnsureCaretVisible();
+ break;
+ case SCI_DELETEBACKNOTLINE:
+ DelCharBack(false);
+ if (!caretSticky) {
+ SetLastXChosen();
+ }
+ EnsureCaretVisible();
+ break;
+ case SCI_TAB:
+ Indent(true);
+ if (!caretSticky) {
+ SetLastXChosen();
+ }
+ EnsureCaretVisible();
+ break;
+ case SCI_BACKTAB:
+ Indent(false);
+ if (!caretSticky) {
+ SetLastXChosen();
+ }
+ EnsureCaretVisible();
+ break;
+ case SCI_NEWLINE:
+ NewLine();
+ break;
+ case SCI_FORMFEED:
+ AddChar('\f');
+ break;
+ case SCI_VCHOME:
+ MovePositionTo(pdoc->VCHomePosition(currentPos));
+ SetLastXChosen();
+ break;
+ case SCI_VCHOMEEXTEND:
+ MovePositionTo(pdoc->VCHomePosition(currentPos), selStream);
+ SetLastXChosen();
+ break;
+ case SCI_VCHOMERECTEXTEND:
+ MovePositionTo(pdoc->VCHomePosition(currentPos), selRectangle);
+ SetLastXChosen();
+ break;
+ case SCI_VCHOMEWRAP: {
+ int homePos = pdoc->VCHomePosition(currentPos);
+ int viewLineStart = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1);
+ if ((viewLineStart < currentPos) && (viewLineStart > homePos))
+ homePos = viewLineStart;
+
+ MovePositionTo(homePos);
+ SetLastXChosen();
+ }
+ break;
+ case SCI_VCHOMEWRAPEXTEND: {
+ int homePos = pdoc->VCHomePosition(currentPos);
+ int viewLineStart = MovePositionSoVisible(StartEndDisplayLine(currentPos, true), -1);
+ if ((viewLineStart < currentPos) && (viewLineStart > homePos))
+ homePos = viewLineStart;
+
+ MovePositionTo(homePos, selStream);
+ SetLastXChosen();
+ }
+ break;
+ case SCI_ZOOMIN:
+ if (vs.zoomLevel < 20) {
+ vs.zoomLevel++;
+ InvalidateStyleRedraw();
+ NotifyZoom();
+ }
+ break;
+ case SCI_ZOOMOUT:
+ if (vs.zoomLevel > -10) {
+ vs.zoomLevel--;
+ InvalidateStyleRedraw();
+ NotifyZoom();
+ }
+ break;
+ case SCI_DELWORDLEFT: {
+ int startWord = pdoc->NextWordStart(currentPos, -1);
+ pdoc->DeleteChars(startWord, currentPos - startWord);
+ SetLastXChosen();
+ }
+ break;
+ case SCI_DELWORDRIGHT: {
+ int endWord = pdoc->NextWordStart(currentPos, 1);
+ pdoc->DeleteChars(currentPos, endWord - currentPos);
+ }
+ break;
+ case SCI_DELLINELEFT: {
+ int line = pdoc->LineFromPosition(currentPos);
+ int start = pdoc->LineStart(line);
+ pdoc->DeleteChars(start, currentPos - start);
+ SetLastXChosen();
+ }
+ break;
+ case SCI_DELLINERIGHT: {
+ int line = pdoc->LineFromPosition(currentPos);
+ int end = pdoc->LineEnd(line);
+ pdoc->DeleteChars(currentPos, end - currentPos);
+ }
+ break;
+ case SCI_LINECOPY: {
+ int lineStart = pdoc->LineFromPosition(SelectionStart());
+ int lineEnd = pdoc->LineFromPosition(SelectionEnd());
+ CopyRangeToClipboard(pdoc->LineStart(lineStart),
+ pdoc->LineStart(lineEnd + 1));
+ }
+ break;
+ case SCI_LINECUT: {
+ int lineStart = pdoc->LineFromPosition(SelectionStart());
+ int lineEnd = pdoc->LineFromPosition(SelectionEnd());
+ int start = pdoc->LineStart(lineStart);
+ int end = pdoc->LineStart(lineEnd + 1);
+ SetSelection(start, end);
+ Cut();
+ SetLastXChosen();
+ }
+ break;
+ case SCI_LINEDELETE: {
+ int line = pdoc->LineFromPosition(currentPos);
+ int start = pdoc->LineStart(line);
+ int end = pdoc->LineStart(line + 1);
+ pdoc->DeleteChars(start, end - start);
+ }
+ break;
+ case SCI_LINETRANSPOSE:
+ LineTranspose();
+ break;
+ case SCI_LINEDUPLICATE:
+ Duplicate(true);
+ break;
+ case SCI_SELECTIONDUPLICATE:
+ Duplicate(false);
+ break;
+ case SCI_LOWERCASE:
+ ChangeCaseOfSelection(false);
+ break;
+ case SCI_UPPERCASE:
+ ChangeCaseOfSelection(true);
+ break;
+ case SCI_WORDPARTLEFT:
+ MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(currentPos), -1));
+ SetLastXChosen();
+ break;
+ case SCI_WORDPARTLEFTEXTEND:
+ MovePositionTo(MovePositionSoVisible(pdoc->WordPartLeft(currentPos), -1), selStream);
+ SetLastXChosen();
+ break;
+ case SCI_WORDPARTRIGHT:
+ MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(currentPos), 1));
+ SetLastXChosen();
+ break;
+ case SCI_WORDPARTRIGHTEXTEND:
+ MovePositionTo(MovePositionSoVisible(pdoc->WordPartRight(currentPos), 1), selStream);
+ SetLastXChosen();
+ break;
+ case SCI_HOMEDISPLAY:
+ MovePositionTo(MovePositionSoVisible(
+ StartEndDisplayLine(currentPos, true), -1));
+ SetLastXChosen();
+ break;
+ case SCI_HOMEDISPLAYEXTEND:
+ MovePositionTo(MovePositionSoVisible(
+ StartEndDisplayLine(currentPos, true), -1), selStream);
+ SetLastXChosen();
+ break;
+ case SCI_LINEENDDISPLAY:
+ MovePositionTo(MovePositionSoVisible(
+ StartEndDisplayLine(currentPos, false), 1));
+ SetLastXChosen();
+ break;
+ case SCI_LINEENDDISPLAYEXTEND:
+ MovePositionTo(MovePositionSoVisible(
+ StartEndDisplayLine(currentPos, false), 1), selStream);
+ SetLastXChosen();
+ break;
+ }
+ return 0;
+}
+
+int Editor::KeyDefault(int, int) {
+ return 0;
+}
+
+int Editor::KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed) {
+ DwellEnd(false);
+ int modifiers = (shift ? SCI_SHIFT : 0) | (ctrl ? SCI_CTRL : 0) |
+ (alt ? SCI_ALT : 0);
+ int msg = kmap.Find(key, modifiers);
+ if (msg) {
+ if (consumed)
+ *consumed = true;
+ return WndProc(msg, 0, 0);
+ } else {
+ if (consumed)
+ *consumed = false;
+ return KeyDefault(key, modifiers);
+ }
+}
+
+void Editor::SetWhitespaceVisible(int view) {
+ vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(view);
+}
+
+int Editor::GetWhitespaceVisible() {
+ return vs.viewWhitespace;
+}
+
+void Editor::Indent(bool forwards) {
+ //Platform::DebugPrintf("INdent %d\n", forwards);
+ int lineOfAnchor = pdoc->LineFromPosition(anchor);
+ int lineCurrentPos = pdoc->LineFromPosition(currentPos);
+ if (lineOfAnchor == lineCurrentPos) {
+ if (forwards) {
+ pdoc->BeginUndoAction();
+ ClearSelection();
+ if (pdoc->GetColumn(currentPos) <= pdoc->GetColumn(pdoc->GetLineIndentPosition(lineCurrentPos)) &&
+ pdoc->tabIndents) {
+ int indentation = pdoc->GetLineIndentation(lineCurrentPos);
+ int indentationStep = pdoc->IndentSize();
+ pdoc->SetLineIndentation(lineCurrentPos, indentation + indentationStep - indentation % indentationStep);
+ SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos));
+ } else {
+ if (pdoc->useTabs) {
+ pdoc->InsertChar(currentPos, '\t');
+ SetEmptySelection(currentPos + 1);
+ } else {
+ int numSpaces = (pdoc->tabInChars) -
+ (pdoc->GetColumn(currentPos) % (pdoc->tabInChars));
+ if (numSpaces < 1)
+ numSpaces = pdoc->tabInChars;
+ for (int i = 0; i < numSpaces; i++) {
+ pdoc->InsertChar(currentPos + i, ' ');
+ }
+ SetEmptySelection(currentPos + numSpaces);
+ }
+ }
+ pdoc->EndUndoAction();
+ } else {
+ if (pdoc->GetColumn(currentPos) <= pdoc->GetLineIndentation(lineCurrentPos) &&
+ pdoc->tabIndents) {
+ pdoc->BeginUndoAction();
+ int indentation = pdoc->GetLineIndentation(lineCurrentPos);
+ int indentationStep = pdoc->IndentSize();
+ pdoc->SetLineIndentation(lineCurrentPos, indentation - indentationStep);
+ SetEmptySelection(pdoc->GetLineIndentPosition(lineCurrentPos));
+ pdoc->EndUndoAction();
+ } else {
+ int newColumn = ((pdoc->GetColumn(currentPos) - 1) / pdoc->tabInChars) *
+ pdoc->tabInChars;
+ if (newColumn < 0)
+ newColumn = 0;
+ int newPos = currentPos;
+ while (pdoc->GetColumn(newPos) > newColumn)
+ newPos--;
+ SetEmptySelection(newPos);
+ }
+ }
+ } else {
+ int anchorPosOnLine = anchor - pdoc->LineStart(lineOfAnchor);
+ int currentPosPosOnLine = currentPos - pdoc->LineStart(lineCurrentPos);
+ // Multiple lines selected so indent / dedent
+ int lineTopSel = Platform::Minimum(lineOfAnchor, lineCurrentPos);
+ int lineBottomSel = Platform::Maximum(lineOfAnchor, lineCurrentPos);
+ if (pdoc->LineStart(lineBottomSel) == anchor || pdoc->LineStart(lineBottomSel) == currentPos)
+ lineBottomSel--; // If not selecting any characters on a line, do not indent
+ pdoc->BeginUndoAction();
+ pdoc->Indent(forwards, lineBottomSel, lineTopSel);
+ pdoc->EndUndoAction();
+ if (lineOfAnchor < lineCurrentPos) {
+ if (currentPosPosOnLine == 0)
+ SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
+ else
+ SetSelection(pdoc->LineStart(lineCurrentPos + 1), pdoc->LineStart(lineOfAnchor));
+ } else {
+ if (anchorPosOnLine == 0)
+ SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor));
+ else
+ SetSelection(pdoc->LineStart(lineCurrentPos), pdoc->LineStart(lineOfAnchor + 1));
+ }
+ }
+}
+
+/**
+ * Search of a text in the document, in the given range.
+ * @return The position of the found text, -1 if not found.
+ */
+long Editor::FindText(
+ uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
+ ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
+ sptr_t lParam) { ///< @c TextToFind structure: The text to search for in the given range.
+
+ TextToFind *ft = reinterpret_cast<TextToFind *>(lParam);
+ int lengthFound = istrlen(ft->lpstrText);
+ int pos = pdoc->FindText(ft->chrg.cpMin, ft->chrg.cpMax, ft->lpstrText,
+ (wParam & SCFIND_MATCHCASE) != 0,
+ (wParam & SCFIND_WHOLEWORD) != 0,
+ (wParam & SCFIND_WORDSTART) != 0,
+ (wParam & SCFIND_REGEXP) != 0,
+ (wParam & SCFIND_POSIX) != 0,
+ &lengthFound);
+ if (pos != -1) {
+ ft->chrgText.cpMin = pos;
+ ft->chrgText.cpMax = pos + lengthFound;
+ }
+ return pos;
+}
+
+/**
+ * Relocatable search support : Searches relative to current selection
+ * point and sets the selection to the found text range with
+ * each search.
+ */
+/**
+ * Anchor following searches at current selection start: This allows
+ * multiple incremental interactive searches to be macro recorded
+ * while still setting the selection to found text so the find/select
+ * operation is self-contained.
+ */
+void Editor::SearchAnchor() {
+ searchAnchor = SelectionStart();
+}
+
+/**
+ * Find text from current search anchor: Must call @c SearchAnchor first.
+ * Used for next text and previous text requests.
+ * @return The position of the found text, -1 if not found.
+ */
+long Editor::SearchText(
+ unsigned int iMessage, ///< Accepts both @c SCI_SEARCHNEXT and @c SCI_SEARCHPREV.
+ uptr_t wParam, ///< Search modes : @c SCFIND_MATCHCASE, @c SCFIND_WHOLEWORD,
+ ///< @c SCFIND_WORDSTART, @c SCFIND_REGEXP or @c SCFIND_POSIX.
+ sptr_t lParam) { ///< The text to search for.
+
+ const char *txt = reinterpret_cast<char *>(lParam);
+ int pos;
+ int lengthFound = istrlen(txt);
+ if (iMessage == SCI_SEARCHNEXT) {
+ pos = pdoc->FindText(searchAnchor, pdoc->Length(), txt,
+ (wParam & SCFIND_MATCHCASE) != 0,
+ (wParam & SCFIND_WHOLEWORD) != 0,
+ (wParam & SCFIND_WORDSTART) != 0,
+ (wParam & SCFIND_REGEXP) != 0,
+ (wParam & SCFIND_POSIX) != 0,
+ &lengthFound);
+ } else {
+ pos = pdoc->FindText(searchAnchor, 0, txt,
+ (wParam & SCFIND_MATCHCASE) != 0,
+ (wParam & SCFIND_WHOLEWORD) != 0,
+ (wParam & SCFIND_WORDSTART) != 0,
+ (wParam & SCFIND_REGEXP) != 0,
+ (wParam & SCFIND_POSIX) != 0,
+ &lengthFound);
+ }
+
+ if (pos != -1) {
+ SetSelection(pos, pos + lengthFound);
+ }
+
+ return pos;
+}
+
+/**
+ * Search for text in the target range of the document.
+ * @return The position of the found text, -1 if not found.
+ */
+long Editor::SearchInTarget(const char *text, int length) {
+ int lengthFound = length;
+ int pos = pdoc->FindText(targetStart, targetEnd, text,
+ (searchFlags & SCFIND_MATCHCASE) != 0,
+ (searchFlags & SCFIND_WHOLEWORD) != 0,
+ (searchFlags & SCFIND_WORDSTART) != 0,
+ (searchFlags & SCFIND_REGEXP) != 0,
+ (searchFlags & SCFIND_POSIX) != 0,
+ &lengthFound);
+ if (pos != -1) {
+ targetStart = pos;
+ targetEnd = pos + lengthFound;
+ }
+ return pos;
+}
+
+void Editor::GoToLine(int lineNo) {
+ if (lineNo > pdoc->LinesTotal())
+ lineNo = pdoc->LinesTotal();
+ if (lineNo < 0)
+ lineNo = 0;
+ SetEmptySelection(pdoc->LineStart(lineNo));
+ ShowCaretAtCurrentPosition();
+ EnsureCaretVisible();
+}
+
+static bool Close(Point pt1, Point pt2) {
+ if (abs(pt1.x - pt2.x) > 3)
+ return false;
+ if (abs(pt1.y - pt2.y) > 3)
+ return false;
+ return true;
+}
+
+char *Editor::CopyRange(int start, int end) {
+ char *text = 0;
+ if (start < end) {
+ int len = end - start;
+ text = new char[len + 1];
+ if (text) {
+ for (int i = 0; i < len; i++) {
+ text[i] = pdoc->CharAt(start + i);
+ }
+ text[len] = '\0';
+ }
+ }
+ return text;
+}
+
+void Editor::CopySelectionFromRange(SelectionText *ss, int start, int end) {
+ ss->Set(CopyRange(start, end), end - start + 1,
+ pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false);
+}
+
+void Editor::CopySelectionRange(SelectionText *ss) {
+ if (selType == selStream) {
+ CopySelectionFromRange(ss, SelectionStart(), SelectionEnd());
+ } else {
+ char *text = 0;
+ int size = 0;
+ SelectionLineIterator lineIterator(this);
+ while (lineIterator.Iterate()) {
+ size += lineIterator.endPos - lineIterator.startPos;
+ if (selType != selLines) {
+ size++;
+ if (pdoc->eolMode == SC_EOL_CRLF) {
+ size++;
+ }
+ }
+ }
+ if (size > 0) {
+ text = new char[size + 1];
+ if (text) {
+ int j = 0;
+ lineIterator.Reset();
+ while (lineIterator.Iterate()) {
+ for (int i = lineIterator.startPos;
+ i < lineIterator.endPos;
+ i++) {
+ text[j++] = pdoc->CharAt(i);
+ }
+ if (selType != selLines) {
+ if (pdoc->eolMode != SC_EOL_LF) {
+ text[j++] = '\r';
+ }
+ if (pdoc->eolMode != SC_EOL_CR) {
+ text[j++] = '\n';
+ }
+ }
+ }
+ text[size] = '\0';
+ }
+ }
+ ss->Set(text, size + 1, pdoc->dbcsCodePage,
+ vs.styles[STYLE_DEFAULT].characterSet, selType == selRectangle);
+ }
+}
+
+void Editor::CopyRangeToClipboard(int start, int end) {
+ start = pdoc->ClampPositionIntoDocument(start);
+ end = pdoc->ClampPositionIntoDocument(end);
+ SelectionText selectedText;
+ selectedText.Set(CopyRange(start, end), end - start + 1,
+ pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false);
+ CopyToClipboard(selectedText);
+}
+
+void Editor::CopyText(int length, const char *text) {
+ SelectionText selectedText;
+ selectedText.Copy(text, length + 1,
+ pdoc->dbcsCodePage, vs.styles[STYLE_DEFAULT].characterSet, false);
+ CopyToClipboard(selectedText);
+}
+
+void Editor::SetDragPosition(int newPos) {
+ if (newPos >= 0) {
+ newPos = MovePositionOutsideChar(newPos, 1);
+ posDrop = newPos;
+ }
+ if (posDrag != newPos) {
+ caret.on = true;
+ SetTicking(true);
+ InvalidateCaret();
+ posDrag = newPos;
+ InvalidateCaret();
+ }
+}
+
+void Editor::DisplayCursor(Window::Cursor c) {
+ if (cursorMode == SC_CURSORNORMAL)
+ wMain.SetCursor(c);
+ else
+ wMain.SetCursor(static_cast<Window::Cursor>(cursorMode));
+}
+
+void Editor::StartDrag() {
+ // Always handled by subclasses
+ //SetMouseCapture(true);
+ //DisplayCursor(Window::cursorArrow);
+}
+
+void Editor::DropAt(int position, const char *value, bool moving, bool rectangular) {
+ //Platform::DebugPrintf("DropAt %d\n", inDragDrop);
+ if (inDragDrop)
+ dropWentOutside = false;
+
+ int positionWasInSelection = PositionInSelection(position);
+
+ bool positionOnEdgeOfSelection =
+ (position == SelectionStart()) || (position == SelectionEnd());
+
+ if ((!inDragDrop) || !(0 == positionWasInSelection) ||
+ (positionOnEdgeOfSelection && !moving)) {
+
+ int selStart = SelectionStart();
+ int selEnd = SelectionEnd();
+
+ pdoc->BeginUndoAction();
+
+ int positionAfterDeletion = position;
+ if (inDragDrop && moving) {
+ // Remove dragged out text
+ if (rectangular || selType == selLines) {
+ SelectionLineIterator lineIterator(this);
+ while (lineIterator.Iterate()) {
+ if (position >= lineIterator.startPos) {
+ if (position > lineIterator.endPos) {
+ positionAfterDeletion -= lineIterator.endPos - lineIterator.startPos;
+ } else {
+ positionAfterDeletion -= position - lineIterator.startPos;
+ }
+ }
+ }
+ } else {
+ if (position > selStart) {
+ positionAfterDeletion -= selEnd - selStart;
+ }
+ }
+ ClearSelection();
+ }
+ position = positionAfterDeletion;
+
+ if (rectangular) {
+ PasteRectangular(position, value, istrlen(value));
+ pdoc->EndUndoAction();
+ // Should try to select new rectangle but it may not be a rectangle now so just select the drop position
+ SetEmptySelection(position);
+ } else {
+ position = MovePositionOutsideChar(position, currentPos - position);
+ if (pdoc->InsertString(position, value)) {
+ SetSelection(position + istrlen(value), position);
+ }
+ pdoc->EndUndoAction();
+ }
+ } else if (inDragDrop) {
+ SetEmptySelection(position);
+ }
+}
+
+/**
+ * @return -1 if given position is before the selection,
+ * 1 if position is after the selection,
+ * 0 if position is inside the selection,
+ */
+int Editor::PositionInSelection(int pos) {
+ pos = MovePositionOutsideChar(pos, currentPos - pos);
+ if (pos < SelectionStart()) {
+ return -1;
+ }
+ if (pos > SelectionEnd()) {
+ return 1;
+ }
+ if (selType == selStream) {
+ return 0;
+ } else {
+ SelectionLineIterator lineIterator(this);
+ lineIterator.SetAt(pdoc->LineFromPosition(pos));
+ if (pos < lineIterator.startPos) {
+ return -1;
+ } else if (pos > lineIterator.endPos) {
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+}
+
+bool Editor::PointInSelection(Point pt) {
+ int pos = PositionFromLocation(pt);
+ if (0 == PositionInSelection(pos)) {
+ // Probably inside, but we must make a finer test
+ int selStart, selEnd;
+ if (selType == selStream) {
+ selStart = SelectionStart();
+ selEnd = SelectionEnd();
+ } else {
+ SelectionLineIterator lineIterator(this);
+ lineIterator.SetAt(pdoc->LineFromPosition(pos));
+ selStart = lineIterator.startPos;
+ selEnd = lineIterator.endPos;
+ }
+ if (pos == selStart) {
+ // see if just before selection
+ Point locStart = LocationFromPosition(pos);
+ if (pt.x < locStart.x) {
+ return false;
+ }
+ }
+ if (pos == selEnd) {
+ // see if just after selection
+ Point locEnd = LocationFromPosition(pos);
+ if (pt.x > locEnd.x) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+bool Editor::PointInSelMargin(Point pt) {
+ // Really means: "Point in a margin"
+ if (vs.fixedColumnWidth > 0) { // There is a margin
+ PRectangle rcSelMargin = GetClientRectangle();
+ rcSelMargin.right = vs.fixedColumnWidth - vs.leftMarginWidth;
+ return rcSelMargin.Contains(pt);
+ } else {
+ return false;
+ }
+}
+
+void Editor::LineSelection(int lineCurrent_, int lineAnchor_) {
+ if (lineAnchor_ < lineCurrent_) {
+ SetSelection(pdoc->LineStart(lineCurrent_ + 1),
+ pdoc->LineStart(lineAnchor_));
+ } else if (lineAnchor_ > lineCurrent_) {
+ SetSelection(pdoc->LineStart(lineCurrent_),
+ pdoc->LineStart(lineAnchor_ + 1));
+ } else { // Same line, select it
+ SetSelection(pdoc->LineStart(lineAnchor_ + 1),
+ pdoc->LineStart(lineAnchor_));
+ }
+}
+
+void Editor::DwellEnd(bool mouseMoved) {
+ if (mouseMoved)
+ ticksToDwell = dwellDelay;
+ else
+ ticksToDwell = SC_TIME_FOREVER;
+ if (dwelling && (dwellDelay < SC_TIME_FOREVER)) {
+ dwelling = false;
+ NotifyDwelling(ptMouseLast, dwelling);
+ }
+}
+
+void Editor::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
+ //Platform::DebugPrintf("Scintilla:ButtonDown %d %d = %d alt=%d\n", curTime, lastClickTime, curTime - lastClickTime, alt);
+ ptMouseLast = pt;
+ int newPos = PositionFromLocation(pt);
+ newPos = MovePositionOutsideChar(newPos, currentPos - newPos);
+ inDragDrop = false;
+ moveExtendsSelection = false;
+
+ bool processed = NotifyMarginClick(pt, shift, ctrl, alt);
+ if (processed)
+ return;
+
+ bool inSelMargin = PointInSelMargin(pt);
+ if (shift & !inSelMargin) {
+ SetSelection(newPos);
+ }
+ if (((curTime - lastClickTime) < Platform::DoubleClickTime()) && Close(pt, lastClick)) {
+ //Platform::DebugPrintf("Double click %d %d = %d\n", curTime, lastClickTime, curTime - lastClickTime);
+ SetMouseCapture(true);
+ SetEmptySelection(newPos);
+ bool doubleClick = false;
+ // Stop mouse button bounce changing selection type
+ if (!Platform::MouseButtonBounce() || curTime != lastClickTime) {
+ if (selectionType == selChar) {
+ selectionType = selWord;
+ doubleClick = true;
+ } else if (selectionType == selWord) {
+ selectionType = selLine;
+ } else {
+ selectionType = selChar;
+ originalAnchorPos = currentPos;
+ }
+ }
+
+ if (selectionType == selWord) {
+ if (currentPos >= originalAnchorPos) { // Moved forward
+ SetSelection(pdoc->ExtendWordSelect(currentPos, 1),
+ pdoc->ExtendWordSelect(originalAnchorPos, -1));
+ } else { // Moved backward
+ SetSelection(pdoc->ExtendWordSelect(currentPos, -1),
+ pdoc->ExtendWordSelect(originalAnchorPos, 1));
+ }
+ } else if (selectionType == selLine) {
+ lineAnchor = LineFromLocation(pt);
+ SetSelection(pdoc->LineStart(lineAnchor + 1), pdoc->LineStart(lineAnchor));
+ //Platform::DebugPrintf("Triple click: %d - %d\n", anchor, currentPos);
+ } else {
+ SetEmptySelection(currentPos);
+ }
+ //Platform::DebugPrintf("Double click: %d - %d\n", anchor, currentPos);
+ if (doubleClick) {
+ NotifyDoubleClick(pt, shift);
+ if (PositionIsHotspot(newPos))
+ NotifyHotSpotDoubleClicked(newPos, shift, ctrl, alt);
+ }
+ } else { // Single click
+ if (inSelMargin) {
+ selType = selStream;
+ if (ctrl) {
+ SelectAll();
+ lastClickTime = curTime;
+ return;
+ }
+ if (!shift) {
+ lineAnchor = LineFromLocation(pt);
+ // Single click in margin: select whole line
+ LineSelection(lineAnchor, lineAnchor);
+ SetSelection(pdoc->LineStart(lineAnchor + 1),
+ pdoc->LineStart(lineAnchor));
+ } else {
+ // Single shift+click in margin: select from line anchor to clicked line
+ if (anchor > currentPos)
+ lineAnchor = pdoc->LineFromPosition(anchor - 1);
+ else
+ lineAnchor = pdoc->LineFromPosition(anchor);
+ int lineStart = LineFromLocation(pt);
+ LineSelection(lineStart, lineAnchor);
+ //lineAnchor = lineStart; // Keep the same anchor for ButtonMove
+ }
+
+ SetDragPosition(invalidPosition);
+ SetMouseCapture(true);
+ selectionType = selLine;
+ } else {
+ if (PointIsHotspot(pt)) {
+ NotifyHotSpotClicked(newPos, shift, ctrl, alt);
+ }
+ if (!shift) {
+ inDragDrop = PointInSelection(pt) && !SelectionEmpty();
+ }
+ if (inDragDrop) {
+ SetMouseCapture(false);
+ SetDragPosition(newPos);
+ CopySelectionRange(&drag);
+ StartDrag();
+ } else {
+ SetDragPosition(invalidPosition);
+ SetMouseCapture(true);
+ if (!shift) {
+ SetEmptySelection(newPos);
+ }
+ selType = alt ? selRectangle : selStream;
+ selectionType = selChar;
+ originalAnchorPos = currentPos;
+ SetRectangularRange();
+ }
+ }
+ }
+ lastClickTime = curTime;
+ lastXChosen = pt.x;
+ ShowCaretAtCurrentPosition();
+}
+
+bool Editor::PositionIsHotspot(int position) {
+ return vs.styles[pdoc->StyleAt(position) & pdoc->stylingBitsMask].hotspot;
+}
+
+bool Editor::PointIsHotspot(Point pt) {
+ int pos = PositionFromLocationClose(pt);
+ if (pos == INVALID_POSITION)
+ return false;
+ return PositionIsHotspot(pos);
+}
+
+void Editor::SetHotSpotRange(Point *pt) {
+ if (pt) {
+ int pos = PositionFromLocation(*pt);
+
+ // If we don't limit this to word characters then the
+ // range can encompass more than the run range and then
+ // the underline will not be drawn properly.
+ int hsStart_ = pdoc->ExtendStyleRange(pos, -1, vs.hotspotSingleLine);
+ int hsEnd_ = pdoc->ExtendStyleRange(pos, 1, vs.hotspotSingleLine);
+
+ // Only invalidate the range if the hotspot range has changed...
+ if (hsStart_ != hsStart || hsEnd_ != hsEnd) {
+ if (hsStart != -1) {
+ InvalidateRange(hsStart, hsEnd);
+ }
+ hsStart = hsStart_;
+ hsEnd = hsEnd_;
+ InvalidateRange(hsStart, hsEnd);
+ }
+ } else {
+ if (hsStart != -1) {
+ int hsStart_ = hsStart;
+ int hsEnd_ = hsEnd;
+ hsStart = -1;
+ hsEnd = -1;
+ InvalidateRange(hsStart_, hsEnd_);
+ } else {
+ hsStart = -1;
+ hsEnd = -1;
+ }
+ }
+}
+
+void Editor::GetHotSpotRange(int& hsStart_, int& hsEnd_) {
+ hsStart_ = hsStart;
+ hsEnd_ = hsEnd;
+}
+
+void Editor::ButtonMove(Point pt) {
+ if ((ptMouseLast.x != pt.x) || (ptMouseLast.y != pt.y)) {
+ DwellEnd(true);
+ }
+ ptMouseLast = pt;
+ //Platform::DebugPrintf("Move %d %d\n", pt.x, pt.y);
+ if (HaveMouseCapture()) {
+
+ // Slow down autoscrolling/selection
+ autoScrollTimer.ticksToWait -= timer.tickSize;
+ if (autoScrollTimer.ticksToWait > 0)
+ return;
+ autoScrollTimer.ticksToWait = autoScrollDelay;
+
+ // Adjust selection
+ int movePos = PositionFromLocation(pt);
+ movePos = MovePositionOutsideChar(movePos, currentPos - movePos);
+ if (posDrag >= 0) {
+ SetDragPosition(movePos);
+ } else {
+ if (selectionType == selChar) {
+ SetSelection(movePos);
+ } else if (selectionType == selWord) {
+ // Continue selecting by word
+ if (movePos == originalAnchorPos) { // Didn't move
+ // No need to do anything. Previously this case was lumped
+ // in with "Moved forward", but that can be harmful in this
+ // case: a handler for the NotifyDoubleClick re-adjusts
+ // the selection for a fancier definition of "word" (for
+ // example, in Perl it is useful to include the leading
+ // '$', '%' or '@' on variables for word selection). In this
+ // the ButtonMove() called via Tick() for auto-scrolling
+ // could result in the fancier word selection adjustment
+ // being unmade.
+ } else if (movePos > originalAnchorPos) { // Moved forward
+ SetSelection(pdoc->ExtendWordSelect(movePos, 1),
+ pdoc->ExtendWordSelect(originalAnchorPos, -1));
+ } else { // Moved backward
+ SetSelection(pdoc->ExtendWordSelect(movePos, -1),
+ pdoc->ExtendWordSelect(originalAnchorPos, 1));
+ }
+ } else {
+ // Continue selecting by line
+ int lineMove = LineFromLocation(pt);
+ LineSelection(lineMove, lineAnchor);
+ }
+ }
+ // While dragging to make rectangular selection, we don't want the current
+ // position to jump to the end of smaller or empty lines.
+ //xEndSelect = pt.x - vs.fixedColumnWidth + xOffset;
+ xEndSelect = XFromPosition(movePos);
+
+ // Autoscroll
+ PRectangle rcClient = GetClientRectangle();
+ if (pt.y > rcClient.bottom) {
+ int lineMove = cs.DisplayFromDoc(LineFromLocation(pt));
+ if (lineMove < 0) {
+ lineMove = cs.DisplayFromDoc(pdoc->LinesTotal() - 1);
+ }
+ ScrollTo(lineMove - LinesOnScreen() + 5);
+ Redraw();
+ } else if (pt.y < rcClient.top) {
+ int lineMove = cs.DisplayFromDoc(LineFromLocation(pt));
+ ScrollTo(lineMove - 5);
+ Redraw();
+ }
+ EnsureCaretVisible(false, false, true);
+
+ if (hsStart != -1 && !PositionIsHotspot(movePos))
+ SetHotSpotRange(NULL);
+
+ } else {
+ if (vs.fixedColumnWidth > 0) { // There is a margin
+ if (PointInSelMargin(pt)) {
+ DisplayCursor(Window::cursorReverseArrow);
+ return; // No need to test for selection
+ }
+ }
+ // Display regular (drag) cursor over selection
+ if (PointInSelection(pt) && !SelectionEmpty()) {
+ DisplayCursor(Window::cursorArrow);
+ } else if (PointIsHotspot(pt)) {
+ DisplayCursor(Window::cursorHand);
+ SetHotSpotRange(&pt);
+ } else {
+ DisplayCursor(Window::cursorText);
+ SetHotSpotRange(NULL);
+ }
+ }
+}
+
+void Editor::ButtonUp(Point pt, unsigned int curTime, bool ctrl) {
+ //Platform::DebugPrintf("ButtonUp %d\n", HaveMouseCapture());
+ if (HaveMouseCapture()) {
+ if (PointInSelMargin(pt)) {
+ DisplayCursor(Window::cursorReverseArrow);
+ } else {
+ DisplayCursor(Window::cursorText);
+ SetHotSpotRange(NULL);
+ }
+ ptMouseLast = pt;
+ SetMouseCapture(false);
+ int newPos = PositionFromLocation(pt);
+ newPos = MovePositionOutsideChar(newPos, currentPos - newPos);
+ if (inDragDrop) {
+ int selStart = SelectionStart();
+ int selEnd = SelectionEnd();
+ if (selStart < selEnd) {
+ if (drag.len) {
+ if (ctrl) {
+ if (pdoc->InsertString(newPos, drag.s, drag.len)) {
+ SetSelection(newPos, newPos + drag.len);
+ }
+ } else if (newPos < selStart) {
+ pdoc->DeleteChars(selStart, drag.len);
+ if (pdoc->InsertString(newPos, drag.s, drag.len)) {
+ SetSelection(newPos, newPos + drag.len);
+ }
+ } else if (newPos > selEnd) {
+ pdoc->DeleteChars(selStart, drag.len);
+ newPos -= drag.len;
+ if (pdoc->InsertString(newPos, drag.s, drag.len)) {
+ SetSelection(newPos, newPos + drag.len);
+ }
+ } else {
+ SetEmptySelection(newPos);
+ }
+ drag.Free();
+ }
+ selectionType = selChar;
+ }
+ } else {
+ if (selectionType == selChar) {
+ SetSelection(newPos);
+ }
+ }
+ SetRectangularRange();
+ lastClickTime = curTime;
+ lastClick = pt;
+ lastXChosen = pt.x;
+ if (selType == selStream) {
+ SetLastXChosen();
+ }
+ inDragDrop = false;
+ EnsureCaretVisible(false);
+ }
+}
+
+// Called frequently to perform background UI including
+// caret blinking and automatic scrolling.
+void Editor::Tick() {
+ if (HaveMouseCapture()) {
+ // Auto scroll
+ ButtonMove(ptMouseLast);
+ }
+ if (caret.period > 0) {
+ timer.ticksToWait -= timer.tickSize;
+ if (timer.ticksToWait <= 0) {
+ caret.on = !caret.on;
+ timer.ticksToWait = caret.period;
+ if (caret.active) {
+ InvalidateCaret();
+ }
+ }
+ }
+ if ((dwellDelay < SC_TIME_FOREVER) &&
+ (ticksToDwell > 0) &&
+ (!HaveMouseCapture())) {
+ ticksToDwell -= timer.tickSize;
+ if (ticksToDwell <= 0) {
+ dwelling = true;
+ NotifyDwelling(ptMouseLast, dwelling);
+ }
+ }
+}
+
+bool Editor::Idle() {
+
+ bool idleDone;
+
+ bool wrappingDone = wrapState == eWrapNone;
+
+ if (!wrappingDone) {
+ // Wrap lines during idle.
+ WrapLines(false, -1);
+ // No more wrapping
+ if (wrapStart == wrapEnd)
+ wrappingDone = true;
+ }
+
+ // Add more idle things to do here, but make sure idleDone is
+ // set correctly before the function returns. returning
+ // false will stop calling this idle funtion until SetIdle() is
+ // called again.
+
+ idleDone = wrappingDone; // && thatDone && theOtherThingDone...
+
+ return !idleDone;
+}
+
+void Editor::SetFocusState(bool focusState) {
+ hasFocus = focusState;
+ NotifyFocus(hasFocus);
+ if (hasFocus) {
+ ShowCaretAtCurrentPosition();
+ } else {
+ CancelModes();
+ DropCaret();
+ }
+}
+
+bool Editor::PaintContains(PRectangle rc) {
+ return rcPaint.Contains(rc);
+}
+
+bool Editor::PaintContainsMargin() {
+ PRectangle rcSelMargin = GetClientRectangle();
+ rcSelMargin.right = vs.fixedColumnWidth;
+ return PaintContains(rcSelMargin);
+}
+
+void Editor::CheckForChangeOutsidePaint(Range r) {
+ if (paintState == painting && !paintingAllText) {
+ //Platform::DebugPrintf("Checking range in paint %d-%d\n", r.start, r.end);
+ if (!r.Valid())
+ return;
+
+ PRectangle rcRange = RectangleFromRange(r.start, r.end);
+ PRectangle rcText = GetTextRectangle();
+ if (rcRange.top < rcText.top) {
+ rcRange.top = rcText.top;
+ }
+ if (rcRange.bottom > rcText.bottom) {
+ rcRange.bottom = rcText.bottom;
+ }
+
+ if (!PaintContains(rcRange)) {
+ AbandonPaint();
+ }
+ }
+}
+
+void Editor::SetBraceHighlight(Position pos0, Position pos1, int matchStyle) {
+ if ((pos0 != braces[0]) || (pos1 != braces[1]) || (matchStyle != bracesMatchStyle)) {
+ if ((braces[0] != pos0) || (matchStyle != bracesMatchStyle)) {
+ CheckForChangeOutsidePaint(Range(braces[0]));
+ CheckForChangeOutsidePaint(Range(pos0));
+ braces[0] = pos0;
+ }
+ if ((braces[1] != pos1) || (matchStyle != bracesMatchStyle)) {
+ CheckForChangeOutsidePaint(Range(braces[1]));
+ CheckForChangeOutsidePaint(Range(pos1));
+ braces[1] = pos1;
+ }
+ bracesMatchStyle = matchStyle;
+ if (paintState == notPainting) {
+ Redraw();
+ }
+ }
+}
+
+void Editor::SetDocPointer(Document *document) {
+ //Platform::DebugPrintf("** %x setdoc to %x\n", pdoc, document);
+ pdoc->RemoveWatcher(this, 0);
+ pdoc->Release();
+ if (document == NULL) {
+ pdoc = new Document();
+ } else {
+ pdoc = document;
+ }
+ pdoc->AddRef();
+
+ // Ensure all positions within document
+ selType = selStream;
+ currentPos = 0;
+ anchor = 0;
+ targetStart = 0;
+ targetEnd = 0;
+
+ braces[0] = invalidPosition;
+ braces[1] = invalidPosition;
+
+ // Reset the contraction state to fully shown.
+ cs.Clear();
+ cs.InsertLines(0, pdoc->LinesTotal() - 1);
+ llc.Deallocate();
+ NeedWrapping();
+
+ pdoc->AddWatcher(this, 0);
+ SetScrollBars();
+ Redraw();
+}
+
+/**
+ * Recursively expand a fold, making lines visible except where they have an unexpanded parent.
+ */
+void Editor::Expand(int &line, bool doExpand) {
+ int lineMaxSubord = pdoc->GetLastChild(line);
+ line++;
+ while (line <= lineMaxSubord) {
+ if (doExpand)
+ cs.SetVisible(line, line, true);
+ int level = pdoc->GetLevel(line);
+ if (level & SC_FOLDLEVELHEADERFLAG) {
+ if (doExpand && cs.GetExpanded(line)) {
+ Expand(line, true);
+ } else {
+ Expand(line, false);
+ }
+ } else {
+ line++;
+ }
+ }
+}
+
+void Editor::ToggleContraction(int line) {
+ if (line >= 0) {
+ if ((pdoc->GetLevel(line) & SC_FOLDLEVELHEADERFLAG) == 0) {
+ line = pdoc->GetFoldParent(line);
+ if (line < 0)
+ return;
+ }
+
+ if (cs.GetExpanded(line)) {
+ int lineMaxSubord = pdoc->GetLastChild(line);
+ cs.SetExpanded(line, 0);
+ if (lineMaxSubord > line) {
+ cs.SetVisible(line + 1, lineMaxSubord, false);
+
+ int lineCurrent = pdoc->LineFromPosition(currentPos);
+ if (lineCurrent > line && lineCurrent <= lineMaxSubord) {
+ // This does not re-expand the fold
+ EnsureCaretVisible();
+ }
+
+ SetScrollBars();
+ Redraw();
+ }
+
+ } else {
+ if (!(cs.GetVisible(line))) {
+ EnsureLineVisible(line, false);
+ GoToLine(line);
+ }
+ cs.SetExpanded(line, 1);
+ Expand(line, true);
+ SetScrollBars();
+ Redraw();
+ }
+ }
+}
+
+/**
+ * Recurse up from this line to find any folds that prevent this line from being visible
+ * and unfold them all.
+ */
+void Editor::EnsureLineVisible(int lineDoc, bool enforcePolicy) {
+
+ // In case in need of wrapping to ensure DisplayFromDoc works.
+ WrapLines(true, -1);
+
+ if (!cs.GetVisible(lineDoc)) {
+ int lineParent = pdoc->GetFoldParent(lineDoc);
+ if (lineParent >= 0) {
+ if (lineDoc != lineParent)
+ EnsureLineVisible(lineParent, enforcePolicy);
+ if (!cs.GetExpanded(lineParent)) {
+ cs.SetExpanded(lineParent, 1);
+ Expand(lineParent, true);
+ }
+ }
+ SetScrollBars();
+ Redraw();
+ }
+ if (enforcePolicy) {
+ int lineDisplay = cs.DisplayFromDoc(lineDoc);
+ if (visiblePolicy & VISIBLE_SLOP) {
+ if ((topLine > lineDisplay) || ((visiblePolicy & VISIBLE_STRICT) && (topLine + visibleSlop > lineDisplay))) {
+ SetTopLine(Platform::Clamp(lineDisplay - visibleSlop, 0, MaxScrollPos()));
+ SetVerticalScrollPos();
+ Redraw();
+ } else if ((lineDisplay > topLine + LinesOnScreen() - 1) ||
+ ((visiblePolicy & VISIBLE_STRICT) && (lineDisplay > topLine + LinesOnScreen() - 1 - visibleSlop))) {
+ SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() + 1 + visibleSlop, 0, MaxScrollPos()));
+ SetVerticalScrollPos();
+ Redraw();
+ }
+ } else {
+ if ((topLine > lineDisplay) || (lineDisplay > topLine + LinesOnScreen() - 1) || (visiblePolicy & VISIBLE_STRICT)) {
+ SetTopLine(Platform::Clamp(lineDisplay - LinesOnScreen() / 2 + 1, 0, MaxScrollPos()));
+ SetVerticalScrollPos();
+ Redraw();
+ }
+ }
+ }
+}
+
+int Editor::ReplaceTarget(bool replacePatterns, const char *text, int length) {
+ pdoc->BeginUndoAction();
+ if (length == -1)
+ length = istrlen(text);
+ if (replacePatterns) {
+ text = pdoc->SubstituteByPosition(text, &length);
+ if (!text)
+ return 0;
+ }
+ if (targetStart != targetEnd)
+ pdoc->DeleteChars(targetStart, targetEnd - targetStart);
+ targetEnd = targetStart;
+ pdoc->InsertString(targetStart, text, length);
+ targetEnd = targetStart + length;
+ pdoc->EndUndoAction();
+ return length;
+}
+
+bool Editor::IsUnicodeMode() const {
+ return pdoc && (SC_CP_UTF8 == pdoc->dbcsCodePage);
+}
+
+int Editor::CodePage() const {
+ if (pdoc)
+ return pdoc->dbcsCodePage;
+ else
+ return 0;
+}
+
+int Editor::WrapCount(int line) {
+ AutoSurface surface(this);
+ AutoLineLayout ll(llc, RetrieveLineLayout(line));
+
+ if (surface && ll) {
+ LayoutLine(line, surface, vs, ll, wrapWidth);
+ return ll->lines;
+ } else {
+ return 1;
+ }
+}
+
+static bool ValidMargin(unsigned long wParam) {
+ return wParam < ViewStyle::margins;
+}
+
+static char *CharPtrFromSPtr(sptr_t lParam) {
+ return reinterpret_cast<char *>(lParam);
+}
+
+sptr_t Editor::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
+ //Platform::DebugPrintf("S start wnd proc %d %d %d\n",iMessage, wParam, lParam);
+
+ // Optional macro recording hook
+ if (recordingMacro)
+ NotifyMacroRecord(iMessage, wParam, lParam);
+
+ switch (iMessage) {
+
+ case SCI_GETTEXT: {
+ if (lParam == 0)
+ return pdoc->Length() + 1;
+ if (wParam == 0)
+ return 0;
+ char *ptr = CharPtrFromSPtr(lParam);
+ unsigned int iChar = 0;
+ for (; iChar < wParam - 1; iChar++)
+ ptr[iChar] = pdoc->CharAt(iChar);
+ ptr[iChar] = '\0';
+ return iChar;
+ }
+
+ case SCI_SETTEXT: {
+ if (lParam == 0)
+ return 0;
+ pdoc->BeginUndoAction();
+ pdoc->DeleteChars(0, pdoc->Length());
+ SetEmptySelection(0);
+ pdoc->InsertString(0, CharPtrFromSPtr(lParam));
+ pdoc->EndUndoAction();
+ return 1;
+ }
+
+ case SCI_GETTEXTLENGTH:
+ return pdoc->Length();
+
+ case SCI_CUT:
+ Cut();
+ SetLastXChosen();
+ break;
+
+ case SCI_COPY:
+ Copy();
+ break;
+
+ case SCI_COPYRANGE:
+ CopyRangeToClipboard(wParam, lParam);
+ break;
+
+ case SCI_COPYTEXT:
+ CopyText(wParam, CharPtrFromSPtr(lParam));
+ break;
+
+ case SCI_PASTE:
+ Paste();
+ if (!caretSticky) {
+ SetLastXChosen();
+ }
+ EnsureCaretVisible();
+ break;
+
+ case SCI_CLEAR:
+ Clear();
+ SetLastXChosen();
+ EnsureCaretVisible();
+ break;
+
+ case SCI_UNDO:
+ Undo();
+ SetLastXChosen();
+ break;
+
+ case SCI_CANUNDO:
+ return (pdoc->CanUndo() && !pdoc->IsReadOnly()) ? 1 : 0;
+
+ case SCI_EMPTYUNDOBUFFER:
+ pdoc->DeleteUndoHistory();
+ return 0;
+
+ case SCI_GETFIRSTVISIBLELINE:
+ return topLine;
+
+ case SCI_GETLINE: { // Risk of overwriting the end of the buffer
+ int lineStart = pdoc->LineStart(wParam);
+ int lineEnd = pdoc->LineStart(wParam + 1);
+ if (lParam == 0) {
+ return lineEnd - lineStart;
+ }
+ char *ptr = CharPtrFromSPtr(lParam);
+ int iPlace = 0;
+ for (int iChar = lineStart; iChar < lineEnd; iChar++) {
+ ptr[iPlace++] = pdoc->CharAt(iChar);
+ }
+ return iPlace;
+ }
+
+ case SCI_GETLINECOUNT:
+ if (pdoc->LinesTotal() == 0)
+ return 1;
+ else
+ return pdoc->LinesTotal();
+
+ case SCI_GETMODIFY:
+ return !pdoc->IsSavePoint();
+
+ case SCI_SETSEL: {
+ int nStart = static_cast<int>(wParam);
+ int nEnd = static_cast<int>(lParam);
+ if (nEnd < 0)
+ nEnd = pdoc->Length();
+ if (nStart < 0)
+ nStart = nEnd; // Remove selection
+ selType = selStream;
+ SetSelection(nEnd, nStart);
+ EnsureCaretVisible();
+ }
+ break;
+
+ case SCI_GETSELTEXT: {
+ if (lParam == 0) {
+ if (selType == selStream) {
+ return 1 + SelectionEnd() - SelectionStart();
+ } else {
+ // TODO: why is selLines handled the slow way?
+ int size = 0;
+ int extraCharsPerLine = 0;
+ if (selType != selLines)
+ extraCharsPerLine = (pdoc->eolMode == SC_EOL_CRLF) ? 2 : 1;
+ SelectionLineIterator lineIterator(this);
+ while (lineIterator.Iterate()) {
+ size += lineIterator.endPos + extraCharsPerLine - lineIterator.startPos;
+ }
+
+ return 1 + size;
+ }
+ }
+ SelectionText selectedText;
+ CopySelectionRange(&selectedText);
+ char *ptr = CharPtrFromSPtr(lParam);
+ int iChar = 0;
+ if (selectedText.len) {
+ for (; iChar < selectedText.len; iChar++)
+ ptr[iChar] = selectedText.s[iChar];
+ } else {
+ ptr[0] = '\0';
+ }
+ return iChar;
+ }
+
+ case SCI_LINEFROMPOSITION:
+ if (static_cast<int>(wParam) < 0)
+ return 0;
+ return pdoc->LineFromPosition(wParam);
+
+ case SCI_POSITIONFROMLINE:
+ if (static_cast<int>(wParam) < 0)
+ wParam = pdoc->LineFromPosition(SelectionStart());
+ if (wParam == 0)
+ return 0; // Even if there is no text, there is a first line that starts at 0
+ if (static_cast<int>(wParam) > pdoc->LinesTotal())
+ return -1;
+ //if (wParam > pdoc->LineFromPosition(pdoc->Length())) // Useful test, anyway...
+ // return -1;
+ return pdoc->LineStart(wParam);
+
+ // Replacement of the old Scintilla interpretation of EM_LINELENGTH
+ case SCI_LINELENGTH:
+ if ((static_cast<int>(wParam) < 0) ||
+ (static_cast<int>(wParam) > pdoc->LineFromPosition(pdoc->Length())))
+ return 0;
+ return pdoc->LineStart(wParam + 1) - pdoc->LineStart(wParam);
+
+ case SCI_REPLACESEL: {
+ if (lParam == 0)
+ return 0;
+ pdoc->BeginUndoAction();
+ ClearSelection();
+ char *replacement = CharPtrFromSPtr(lParam);
+ pdoc->InsertString(currentPos, replacement);
+ pdoc->EndUndoAction();
+ SetEmptySelection(currentPos + istrlen(replacement));
+ EnsureCaretVisible();
+ }
+ break;
+
+ case SCI_SETTARGETSTART:
+ targetStart = wParam;
+ break;
+
+ case SCI_GETTARGETSTART:
+ return targetStart;
+
+ case SCI_SETTARGETEND:
+ targetEnd = wParam;
+ break;
+
+ case SCI_GETTARGETEND:
+ return targetEnd;
+
+ case SCI_TARGETFROMSELECTION:
+ if (currentPos < anchor) {
+ targetStart = currentPos;
+ targetEnd = anchor;
+ } else {
+ targetStart = anchor;
+ targetEnd = currentPos;
+ }
+ break;
+
+ case SCI_REPLACETARGET:
+ PLATFORM_ASSERT(lParam);
+ return ReplaceTarget(false, CharPtrFromSPtr(lParam), wParam);
+
+ case SCI_REPLACETARGETRE:
+ PLATFORM_ASSERT(lParam);
+ return ReplaceTarget(true, CharPtrFromSPtr(lParam), wParam);
+
+ case SCI_SEARCHINTARGET:
+ PLATFORM_ASSERT(lParam);
+ return SearchInTarget(CharPtrFromSPtr(lParam), wParam);
+
+ case SCI_SETSEARCHFLAGS:
+ searchFlags = wParam;
+ break;
+
+ case SCI_GETSEARCHFLAGS:
+ return searchFlags;
+
+ case SCI_POSITIONBEFORE:
+ return pdoc->MovePositionOutsideChar(wParam-1, -1, true);
+
+ case SCI_POSITIONAFTER:
+ return pdoc->MovePositionOutsideChar(wParam+1, 1, true);
+
+ case SCI_LINESCROLL:
+ ScrollTo(topLine + lParam);
+ HorizontalScrollTo(xOffset + wParam * vs.spaceWidth);
+ return 1;
+
+ case SCI_SETXOFFSET:
+ xOffset = wParam;
+ SetHorizontalScrollPos();
+ Redraw();
+ break;
+
+ case SCI_GETXOFFSET:
+ return xOffset;
+
+ case SCI_CHOOSECARETX:
+ SetLastXChosen();
+ break;
+
+ case SCI_SCROLLCARET:
+ EnsureCaretVisible();
+ break;
+
+ case SCI_SETREADONLY:
+ pdoc->SetReadOnly(wParam != 0);
+ return 1;
+
+ case SCI_GETREADONLY:
+ return pdoc->IsReadOnly();
+
+ case SCI_CANPASTE:
+ return CanPaste();
+
+ case SCI_POINTXFROMPOSITION:
+ if (lParam < 0) {
+ return 0;
+ } else {
+ Point pt = LocationFromPosition(lParam);
+ return pt.x;
+ }
+
+ case SCI_POINTYFROMPOSITION:
+ if (lParam < 0) {
+ return 0;
+ } else {
+ Point pt = LocationFromPosition(lParam);
+ return pt.y;
+ }
+
+ case SCI_FINDTEXT:
+ return FindText(wParam, lParam);
+
+ case SCI_GETTEXTRANGE: {
+ if (lParam == 0)
+ return 0;
+ TextRange *tr = reinterpret_cast<TextRange *>(lParam);
+ int cpMax = tr->chrg.cpMax;
+ if (cpMax == -1)
+ cpMax = pdoc->Length();
+ PLATFORM_ASSERT(cpMax <= pdoc->Length());
+ int len = cpMax - tr->chrg.cpMin; // No -1 as cpMin and cpMax are referring to inter character positions
+ pdoc->GetCharRange(tr->lpstrText, tr->chrg.cpMin, len);
+ // Spec says copied text is terminated with a NUL
+ tr->lpstrText[len] = '\0';
+ return len; // Not including NUL
+ }
+
+ case SCI_HIDESELECTION:
+ hideSelection = wParam != 0;
+ Redraw();
+ break;
+
+ case SCI_FORMATRANGE:
+ return FormatRange(wParam != 0, reinterpret_cast<RangeToFormat *>(lParam));
+
+ case SCI_GETMARGINLEFT:
+ return vs.leftMarginWidth;
+
+ case SCI_GETMARGINRIGHT:
+ return vs.rightMarginWidth;
+
+ case SCI_SETMARGINLEFT:
+ vs.leftMarginWidth = lParam;
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_SETMARGINRIGHT:
+ vs.rightMarginWidth = lParam;
+ InvalidateStyleRedraw();
+ break;
+
+ // Control specific mesages
+
+ case SCI_ADDTEXT: {
+ if (lParam == 0)
+ return 0;
+ pdoc->InsertString(CurrentPosition(), CharPtrFromSPtr(lParam), wParam);
+ SetEmptySelection(currentPos + wParam);
+ return 0;
+ }
+
+ case SCI_ADDSTYLEDTEXT: {
+ if (lParam == 0)
+ return 0;
+ pdoc->InsertStyledString(CurrentPosition() * 2, CharPtrFromSPtr(lParam), wParam);
+ SetEmptySelection(currentPos + wParam / 2);
+ return 0;
+ }
+
+ case SCI_INSERTTEXT: {
+ if (lParam == 0)
+ return 0;
+ int insertPos = wParam;
+ if (static_cast<int>(wParam) == -1)
+ insertPos = CurrentPosition();
+ int newCurrent = CurrentPosition();
+ char *sz = CharPtrFromSPtr(lParam);
+ pdoc->InsertString(insertPos, sz);
+ if (newCurrent > insertPos)
+ newCurrent += istrlen(sz);
+ SetEmptySelection(newCurrent);
+ return 0;
+ }
+
+ case SCI_APPENDTEXT:
+ pdoc->InsertString(pdoc->Length(), CharPtrFromSPtr(lParam), wParam);
+ return 0;
+
+ case SCI_CLEARALL:
+ ClearAll();
+ return 0;
+
+ case SCI_CLEARDOCUMENTSTYLE:
+ ClearDocumentStyle();
+ return 0;
+
+ case SCI_SETUNDOCOLLECTION:
+ pdoc->SetUndoCollection(wParam != 0);
+ return 0;
+
+ case SCI_GETUNDOCOLLECTION:
+ return pdoc->IsCollectingUndo();
+
+ case SCI_BEGINUNDOACTION:
+ pdoc->BeginUndoAction();
+ return 0;
+
+ case SCI_ENDUNDOACTION:
+ pdoc->EndUndoAction();
+ return 0;
+
+ case SCI_GETCARETPERIOD:
+ return caret.period;
+
+ case SCI_SETCARETPERIOD:
+ caret.period = wParam;
+ break;
+
+ case SCI_SETWORDCHARS: {
+ pdoc->SetDefaultCharClasses(false);
+ if (lParam == 0)
+ return 0;
+ pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccWord);
+ }
+ break;
+
+ case SCI_SETWHITESPACECHARS: {
+ if (lParam == 0)
+ return 0;
+ pdoc->SetCharClasses(reinterpret_cast<unsigned char *>(lParam), CharClassify::ccSpace);
+ }
+ break;
+
+ case SCI_SETCHARSDEFAULT:
+ pdoc->SetDefaultCharClasses(true);
+ break;
+
+ case SCI_GETLENGTH:
+ return pdoc->Length();
+
+ case SCI_ALLOCATE:
+ pdoc->Allocate(wParam);
+ break;
+
+ case SCI_GETCHARAT:
+ return pdoc->CharAt(wParam);
+
+ case SCI_SETCURRENTPOS:
+ SetSelection(wParam, anchor);
+ break;
+
+ case SCI_GETCURRENTPOS:
+ return currentPos;
+
+ case SCI_SETANCHOR:
+ SetSelection(currentPos, wParam);
+ break;
+
+ case SCI_GETANCHOR:
+ return anchor;
+
+ case SCI_SETSELECTIONSTART:
+ SetSelection(Platform::Maximum(currentPos, wParam), wParam);
+ break;
+
+ case SCI_GETSELECTIONSTART:
+ return Platform::Minimum(anchor, currentPos);
+
+ case SCI_SETSELECTIONEND:
+ SetSelection(wParam, Platform::Minimum(anchor, wParam));
+ break;
+
+ case SCI_GETSELECTIONEND:
+ return Platform::Maximum(anchor, currentPos);
+
+ case SCI_SETPRINTMAGNIFICATION:
+ printMagnification = wParam;
+ break;
+
+ case SCI_GETPRINTMAGNIFICATION:
+ return printMagnification;
+
+ case SCI_SETPRINTCOLOURMODE:
+ printColourMode = wParam;
+ break;
+
+ case SCI_GETPRINTCOLOURMODE:
+ return printColourMode;
+
+ case SCI_SETPRINTWRAPMODE:
+ printWrapState = (wParam == SC_WRAP_WORD) ? eWrapWord : eWrapNone;
+ break;
+
+ case SCI_GETPRINTWRAPMODE:
+ return printWrapState;
+
+ case SCI_GETSTYLEAT:
+ if (static_cast<int>(wParam) >= pdoc->Length())
+ return 0;
+ else
+ return pdoc->StyleAt(wParam);
+
+ case SCI_REDO:
+ Redo();
+ break;
+
+ case SCI_SELECTALL:
+ SelectAll();
+ break;
+
+ case SCI_SETSAVEPOINT:
+ pdoc->SetSavePoint();
+ break;
+
+ case SCI_GETSTYLEDTEXT: {
+ if (lParam == 0)
+ return 0;
+ TextRange *tr = reinterpret_cast<TextRange *>(lParam);
+ int iPlace = 0;
+ for (int iChar = tr->chrg.cpMin; iChar < tr->chrg.cpMax; iChar++) {
+ tr->lpstrText[iPlace++] = pdoc->CharAt(iChar);
+ tr->lpstrText[iPlace++] = pdoc->StyleAt(iChar);
+ }
+ tr->lpstrText[iPlace] = '\0';
+ tr->lpstrText[iPlace + 1] = '\0';
+ return iPlace;
+ }
+
+ case SCI_CANREDO:
+ return (pdoc->CanRedo() && !pdoc->IsReadOnly()) ? 1 : 0;
+
+ case SCI_MARKERLINEFROMHANDLE:
+ return pdoc->LineFromHandle(wParam);
+
+ case SCI_MARKERDELETEHANDLE:
+ pdoc->DeleteMarkFromHandle(wParam);
+ break;
+
+ case SCI_GETVIEWWS:
+ return vs.viewWhitespace;
+
+ case SCI_SETVIEWWS:
+ vs.viewWhitespace = static_cast<WhiteSpaceVisibility>(wParam);
+ Redraw();
+ break;
+
+ case SCI_POSITIONFROMPOINT:
+ return PositionFromLocation(Point(wParam, lParam));
+
+ case SCI_POSITIONFROMPOINTCLOSE:
+ return PositionFromLocationClose(Point(wParam, lParam));
+
+ case SCI_GOTOLINE:
+ GoToLine(wParam);
+ break;
+
+ case SCI_GOTOPOS:
+ SetEmptySelection(wParam);
+ EnsureCaretVisible();
+ Redraw();
+ break;
+
+ case SCI_GETCURLINE: {
+ int lineCurrentPos = pdoc->LineFromPosition(currentPos);
+ int lineStart = pdoc->LineStart(lineCurrentPos);
+ unsigned int lineEnd = pdoc->LineStart(lineCurrentPos + 1);
+ if (lParam == 0) {
+ return 1 + lineEnd - lineStart;
+ }
+ PLATFORM_ASSERT(wParam > 0);
+ char *ptr = CharPtrFromSPtr(lParam);
+ unsigned int iPlace = 0;
+ for (unsigned int iChar = lineStart; iChar < lineEnd && iPlace < wParam - 1; iChar++) {
+ ptr[iPlace++] = pdoc->CharAt(iChar);
+ }
+ ptr[iPlace] = '\0';
+ return currentPos - lineStart;
+ }
+
+ case SCI_GETENDSTYLED:
+ return pdoc->GetEndStyled();
+
+ case SCI_GETEOLMODE:
+ return pdoc->eolMode;
+
+ case SCI_SETEOLMODE:
+ pdoc->eolMode = wParam;
+ break;
+
+ case SCI_STARTSTYLING:
+ pdoc->StartStyling(wParam, static_cast<char>(lParam));
+ break;
+
+ case SCI_SETSTYLING:
+ pdoc->SetStyleFor(wParam, static_cast<char>(lParam));
+ break;
+
+ case SCI_SETSTYLINGEX: // Specify a complete styling buffer
+ if (lParam == 0)
+ return 0;
+ pdoc->SetStyles(wParam, CharPtrFromSPtr(lParam));
+ break;
+
+ case SCI_SETBUFFEREDDRAW:
+ bufferedDraw = wParam != 0;
+ break;
+
+ case SCI_GETBUFFEREDDRAW:
+ return bufferedDraw;
+
+ case SCI_GETTWOPHASEDRAW:
+ return twoPhaseDraw;
+
+ case SCI_SETTWOPHASEDRAW:
+ twoPhaseDraw = wParam != 0;
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_SETTABWIDTH:
+ if (wParam > 0) {
+ pdoc->tabInChars = wParam;
+ if (pdoc->indentInChars == 0)
+ pdoc->actualIndentInChars = pdoc->tabInChars;
+ }
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_GETTABWIDTH:
+ return pdoc->tabInChars;
+
+ case SCI_SETINDENT:
+ pdoc->indentInChars = wParam;
+ if (pdoc->indentInChars != 0)
+ pdoc->actualIndentInChars = pdoc->indentInChars;
+ else
+ pdoc->actualIndentInChars = pdoc->tabInChars;
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_GETINDENT:
+ return pdoc->indentInChars;
+
+ case SCI_SETUSETABS:
+ pdoc->useTabs = wParam != 0;
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_GETUSETABS:
+ return pdoc->useTabs;
+
+ case SCI_SETLINEINDENTATION:
+ pdoc->SetLineIndentation(wParam, lParam);
+ break;
+
+ case SCI_GETLINEINDENTATION:
+ return pdoc->GetLineIndentation(wParam);
+
+ case SCI_GETLINEINDENTPOSITION:
+ return pdoc->GetLineIndentPosition(wParam);
+
+ case SCI_SETTABINDENTS:
+ pdoc->tabIndents = wParam != 0;
+ break;
+
+ case SCI_GETTABINDENTS:
+ return pdoc->tabIndents;
+
+ case SCI_SETBACKSPACEUNINDENTS:
+ pdoc->backspaceUnindents = wParam != 0;
+ break;
+
+ case SCI_GETBACKSPACEUNINDENTS:
+ return pdoc->backspaceUnindents;
+
+ case SCI_SETMOUSEDWELLTIME:
+ dwellDelay = wParam;
+ ticksToDwell = dwellDelay;
+ break;
+
+ case SCI_GETMOUSEDWELLTIME:
+ return dwellDelay;
+
+ case SCI_WORDSTARTPOSITION:
+ return pdoc->ExtendWordSelect(wParam, -1, lParam != 0);
+
+ case SCI_WORDENDPOSITION:
+ return pdoc->ExtendWordSelect(wParam, 1, lParam != 0);
+
+ case SCI_SETWRAPMODE:
+ switch (wParam) {
+ case SC_WRAP_WORD:
+ wrapState = eWrapWord;
+ break;
+ case SC_WRAP_CHAR:
+ wrapState = eWrapChar;
+ break;
+ default:
+ wrapState = eWrapNone;
+ break;
+ }
+ xOffset = 0;
+ InvalidateStyleRedraw();
+ ReconfigureScrollBars();
+ break;
+
+ case SCI_GETWRAPMODE:
+ return wrapState;
+
+ case SCI_SETWRAPVISUALFLAGS:
+ wrapVisualFlags = wParam;
+ actualWrapVisualStartIndent = wrapVisualStartIndent;
+ if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (actualWrapVisualStartIndent == 0))
+ actualWrapVisualStartIndent = 1; // must indent to show start visual
+ InvalidateStyleRedraw();
+ ReconfigureScrollBars();
+ break;
+
+ case SCI_GETWRAPVISUALFLAGS:
+ return wrapVisualFlags;
+
+ case SCI_SETWRAPVISUALFLAGSLOCATION:
+ wrapVisualFlagsLocation = wParam;
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_GETWRAPVISUALFLAGSLOCATION:
+ return wrapVisualFlagsLocation;
+
+ case SCI_SETWRAPSTARTINDENT:
+ wrapVisualStartIndent = wParam;
+ actualWrapVisualStartIndent = wrapVisualStartIndent;
+ if ((wrapVisualFlags & SC_WRAPVISUALFLAG_START) && (actualWrapVisualStartIndent == 0))
+ actualWrapVisualStartIndent = 1; // must indent to show start visual
+ InvalidateStyleRedraw();
+ ReconfigureScrollBars();
+ break;
+
+ case SCI_GETWRAPSTARTINDENT:
+ return wrapVisualStartIndent;
+
+ case SCI_SETLAYOUTCACHE:
+ llc.SetLevel(wParam);
+ break;
+
+ case SCI_GETLAYOUTCACHE:
+ return llc.GetLevel();
+
+ case SCI_SETSCROLLWIDTH:
+ PLATFORM_ASSERT(wParam > 0);
+ if ((wParam > 0) && (wParam != static_cast<unsigned int >(scrollWidth))) {
+ scrollWidth = wParam;
+ SetScrollBars();
+ }
+ break;
+
+ case SCI_GETSCROLLWIDTH:
+ return scrollWidth;
+
+ case SCI_LINESJOIN:
+ LinesJoin();
+ break;
+
+ case SCI_LINESSPLIT:
+ LinesSplit(wParam);
+ break;
+
+ case SCI_TEXTWIDTH:
+ PLATFORM_ASSERT(wParam <= STYLE_MAX);
+ PLATFORM_ASSERT(lParam);
+ return TextWidth(wParam, CharPtrFromSPtr(lParam));
+
+ case SCI_TEXTHEIGHT:
+ return vs.lineHeight;
+
+ case SCI_SETENDATLASTLINE:
+ PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
+ if (endAtLastLine != (wParam != 0)) {
+ endAtLastLine = wParam != 0;
+ SetScrollBars();
+ }
+ break;
+
+ case SCI_GETENDATLASTLINE:
+ return endAtLastLine;
+
+ case SCI_SETCARETSTICKY:
+ PLATFORM_ASSERT((wParam == 0) || (wParam == 1));
+ if (caretSticky != (wParam != 0)) {
+ caretSticky = wParam != 0;
+ }
+ break;
+
+ case SCI_GETCARETSTICKY:
+ return caretSticky;
+
+ case SCI_TOGGLECARETSTICKY:
+ caretSticky = !caretSticky;
+ break;
+
+ case SCI_GETCOLUMN:
+ return pdoc->GetColumn(wParam);
+
+ case SCI_FINDCOLUMN:
+ return pdoc->FindColumn(wParam, lParam);
+
+ case SCI_SETHSCROLLBAR :
+ if (horizontalScrollBarVisible != (wParam != 0)) {
+ horizontalScrollBarVisible = wParam != 0;
+ SetScrollBars();
+ ReconfigureScrollBars();
+ }
+ break;
+
+ case SCI_GETHSCROLLBAR:
+ return horizontalScrollBarVisible;
+
+ case SCI_SETVSCROLLBAR:
+ if (verticalScrollBarVisible != (wParam != 0)) {
+ verticalScrollBarVisible = wParam != 0;
+ SetScrollBars();
+ ReconfigureScrollBars();
+ }
+ break;
+
+ case SCI_GETVSCROLLBAR:
+ return verticalScrollBarVisible;
+
+ case SCI_SETINDENTATIONGUIDES:
+ vs.viewIndentationGuides = wParam != 0;
+ Redraw();
+ break;
+
+ case SCI_GETINDENTATIONGUIDES:
+ return vs.viewIndentationGuides;
+
+ case SCI_SETHIGHLIGHTGUIDE:
+ if ((highlightGuideColumn != static_cast<int>(wParam)) || (wParam > 0)) {
+ highlightGuideColumn = wParam;
+ Redraw();
+ }
+ break;
+
+ case SCI_GETHIGHLIGHTGUIDE:
+ return highlightGuideColumn;
+
+ case SCI_GETLINEENDPOSITION:
+ return pdoc->LineEnd(wParam);
+
+ case SCI_SETCODEPAGE:
+ if (ValidCodePage(wParam)) {
+ pdoc->dbcsCodePage = wParam;
+ InvalidateStyleRedraw();
+ }
+ break;
+
+ case SCI_GETCODEPAGE:
+ return pdoc->dbcsCodePage;
+
+ case SCI_SETUSEPALETTE:
+ palette.allowRealization = wParam != 0;
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_GETUSEPALETTE:
+ return palette.allowRealization;
+
+ // Marker definition and setting
+ case SCI_MARKERDEFINE:
+ if (wParam <= MARKER_MAX)
+ vs.markers[wParam].markType = lParam;
+ InvalidateStyleData();
+ RedrawSelMargin();
+ break;
+ case SCI_MARKERSETFORE:
+ if (wParam <= MARKER_MAX)
+ vs.markers[wParam].fore.desired = ColourDesired(lParam);
+ InvalidateStyleData();
+ RedrawSelMargin();
+ break;
+ case SCI_MARKERSETBACK:
+ if (wParam <= MARKER_MAX)
+ vs.markers[wParam].back.desired = ColourDesired(lParam);
+ InvalidateStyleData();
+ RedrawSelMargin();
+ break;
+ case SCI_MARKERSETALPHA:
+ if (wParam <= MARKER_MAX)
+ vs.markers[wParam].alpha = lParam;
+ InvalidateStyleRedraw();
+ break;
+ case SCI_MARKERADD: {
+ int markerID = pdoc->AddMark(wParam, lParam);
+ return markerID;
+ }
+ case SCI_MARKERADDSET:
+ if (lParam != 0)
+ pdoc->AddMarkSet(wParam, lParam);
+ break;
+
+ case SCI_MARKERDELETE:
+ pdoc->DeleteMark(wParam, lParam);
+ break;
+
+ case SCI_MARKERDELETEALL:
+ pdoc->DeleteAllMarks(static_cast<int>(wParam));
+ break;
+
+ case SCI_MARKERGET:
+ return pdoc->GetMark(wParam);
+
+ case SCI_MARKERNEXT: {
+ int lt = pdoc->LinesTotal();
+ for (int iLine = wParam; iLine < lt; iLine++) {
+ if ((pdoc->GetMark(iLine) & lParam) != 0)
+ return iLine;
+ }
+ }
+ return -1;
+
+ case SCI_MARKERPREVIOUS: {
+ for (int iLine = wParam; iLine >= 0; iLine--) {
+ if ((pdoc->GetMark(iLine) & lParam) != 0)
+ return iLine;
+ }
+ }
+ return -1;
+
+ case SCI_MARKERDEFINEPIXMAP:
+ if (wParam <= MARKER_MAX) {
+ vs.markers[wParam].SetXPM(CharPtrFromSPtr(lParam));
+ };
+ InvalidateStyleData();
+ RedrawSelMargin();
+ break;
+
+ case SCI_SETMARGINTYPEN:
+ if (ValidMargin(wParam)) {
+ vs.ms[wParam].style = lParam;
+ InvalidateStyleRedraw();
+ }
+ break;
+
+ case SCI_GETMARGINTYPEN:
+ if (ValidMargin(wParam))
+ return vs.ms[wParam].style;
+ else
+ return 0;
+
+ case SCI_SETMARGINWIDTHN:
+ if (ValidMargin(wParam)) {
+ // Short-circuit if the width is unchanged, to avoid unnecessary redraw.
+ if (vs.ms[wParam].width != lParam) {
+ vs.ms[wParam].width = lParam;
+ InvalidateStyleRedraw();
+ }
+ }
+ break;
+
+ case SCI_GETMARGINWIDTHN:
+ if (ValidMargin(wParam))
+ return vs.ms[wParam].width;
+ else
+ return 0;
+
+ case SCI_SETMARGINMASKN:
+ if (ValidMargin(wParam)) {
+ vs.ms[wParam].mask = lParam;
+ InvalidateStyleRedraw();
+ }
+ break;
+
+ case SCI_GETMARGINMASKN:
+ if (ValidMargin(wParam))
+ return vs.ms[wParam].mask;
+ else
+ return 0;
+
+ case SCI_SETMARGINSENSITIVEN:
+ if (ValidMargin(wParam)) {
+ vs.ms[wParam].sensitive = lParam != 0;
+ InvalidateStyleRedraw();
+ }
+ break;
+
+ case SCI_GETMARGINSENSITIVEN:
+ if (ValidMargin(wParam))
+ return vs.ms[wParam].sensitive ? 1 : 0;
+ else
+ return 0;
+
+ case SCI_STYLECLEARALL:
+ vs.ClearStyles();
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_STYLESETFORE:
+ if (wParam <= STYLE_MAX) {
+ vs.styles[wParam].fore.desired = ColourDesired(lParam);
+ InvalidateStyleRedraw();
+ }
+ break;
+ case SCI_STYLESETBACK:
+ if (wParam <= STYLE_MAX) {
+ vs.styles[wParam].back.desired = ColourDesired(lParam);
+ InvalidateStyleRedraw();
+ }
+ break;
+ case SCI_STYLESETBOLD:
+ if (wParam <= STYLE_MAX) {
+ vs.styles[wParam].bold = lParam != 0;
+ InvalidateStyleRedraw();
+ }
+ break;
+ case SCI_STYLESETITALIC:
+ if (wParam <= STYLE_MAX) {
+ vs.styles[wParam].italic = lParam != 0;
+ InvalidateStyleRedraw();
+ }
+ break;
+ case SCI_STYLESETEOLFILLED:
+ if (wParam <= STYLE_MAX) {
+ vs.styles[wParam].eolFilled = lParam != 0;
+ InvalidateStyleRedraw();
+ }
+ break;
+ case SCI_STYLESETSIZE:
+ if (wParam <= STYLE_MAX) {
+ vs.styles[wParam].size = lParam;
+ InvalidateStyleRedraw();
+ }
+ break;
+ case SCI_STYLESETFONT:
+ if (lParam == 0)
+ return 0;
+ if (wParam <= STYLE_MAX) {
+ vs.SetStyleFontName(wParam, CharPtrFromSPtr(lParam));
+ InvalidateStyleRedraw();
+ }
+ break;
+ case SCI_STYLESETUNDERLINE:
+ if (wParam <= STYLE_MAX) {
+ vs.styles[wParam].underline = lParam != 0;
+ InvalidateStyleRedraw();
+ }
+ break;
+ case SCI_STYLESETCASE:
+ if (wParam <= STYLE_MAX) {
+ vs.styles[wParam].caseForce = static_cast<Style::ecaseForced>(lParam);
+ InvalidateStyleRedraw();
+ }
+ break;
+ case SCI_STYLESETCHARACTERSET:
+ if (wParam <= STYLE_MAX) {
+ vs.styles[wParam].characterSet = lParam;
+ InvalidateStyleRedraw();
+ }
+ break;
+ case SCI_STYLESETVISIBLE:
+ if (wParam <= STYLE_MAX) {
+ vs.styles[wParam].visible = lParam != 0;
+ InvalidateStyleRedraw();
+ }
+ break;
+ case SCI_STYLESETCHANGEABLE:
+ if (wParam <= STYLE_MAX) {
+ vs.styles[wParam].changeable = lParam != 0;
+ InvalidateStyleRedraw();
+ }
+ break;
+ case SCI_STYLESETHOTSPOT:
+ if (wParam <= STYLE_MAX) {
+ vs.styles[wParam].hotspot = lParam != 0;
+ InvalidateStyleRedraw();
+ }
+ break;
+
+ case SCI_STYLERESETDEFAULT:
+ vs.ResetDefaultStyle();
+ InvalidateStyleRedraw();
+ break;
+ case SCI_SETSTYLEBITS:
+ pdoc->SetStylingBits(wParam);
+ break;
+
+ case SCI_GETSTYLEBITS:
+ return pdoc->stylingBits;
+
+ case SCI_SETLINESTATE:
+ return pdoc->SetLineState(wParam, lParam);
+
+ case SCI_GETLINESTATE:
+ return pdoc->GetLineState(wParam);
+
+ case SCI_GETMAXLINESTATE:
+ return pdoc->GetMaxLineState();
+
+ case SCI_GETCARETLINEVISIBLE:
+ return vs.showCaretLineBackground;
+ case SCI_SETCARETLINEVISIBLE:
+ vs.showCaretLineBackground = wParam != 0;
+ InvalidateStyleRedraw();
+ break;
+ case SCI_GETCARETLINEBACK:
+ return vs.caretLineBackground.desired.AsLong();
+ case SCI_SETCARETLINEBACK:
+ vs.caretLineBackground.desired = wParam;
+ InvalidateStyleRedraw();
+ break;
+ case SCI_GETCARETLINEBACKALPHA:
+ return vs.caretLineAlpha;
+ case SCI_SETCARETLINEBACKALPHA:
+ vs.caretLineAlpha = wParam;
+ InvalidateStyleRedraw();
+ break;
+
+ // Folding messages
+
+ case SCI_VISIBLEFROMDOCLINE:
+ return cs.DisplayFromDoc(wParam);
+
+ case SCI_DOCLINEFROMVISIBLE:
+ return cs.DocFromDisplay(wParam);
+
+ case SCI_WRAPCOUNT:
+ return WrapCount(wParam);
+
+ case SCI_SETFOLDLEVEL: {
+ int prev = pdoc->SetLevel(wParam, lParam);
+ if (prev != lParam)
+ RedrawSelMargin();
+ return prev;
+ }
+
+ case SCI_GETFOLDLEVEL:
+ return pdoc->GetLevel(wParam);
+
+ case SCI_GETLASTCHILD:
+ return pdoc->GetLastChild(wParam, lParam);
+
+ case SCI_GETFOLDPARENT:
+ return pdoc->GetFoldParent(wParam);
+
+ case SCI_SHOWLINES:
+ cs.SetVisible(wParam, lParam, true);
+ SetScrollBars();
+ Redraw();
+ break;
+
+ case SCI_HIDELINES:
+ cs.SetVisible(wParam, lParam, false);
+ SetScrollBars();
+ Redraw();
+ break;
+
+ case SCI_GETLINEVISIBLE:
+ return cs.GetVisible(wParam);
+
+ case SCI_SETFOLDEXPANDED:
+ if (cs.SetExpanded(wParam, lParam != 0)) {
+ RedrawSelMargin();
+ }
+ break;
+
+ case SCI_GETFOLDEXPANDED:
+ return cs.GetExpanded(wParam);
+
+ case SCI_SETFOLDFLAGS:
+ foldFlags = wParam;
+ Redraw();
+ break;
+
+ case SCI_TOGGLEFOLD:
+ ToggleContraction(wParam);
+ break;
+
+ case SCI_ENSUREVISIBLE:
+ EnsureLineVisible(wParam, false);
+ break;
+
+ case SCI_ENSUREVISIBLEENFORCEPOLICY:
+ EnsureLineVisible(wParam, true);
+ break;
+
+ case SCI_SEARCHANCHOR:
+ SearchAnchor();
+ break;
+
+ case SCI_SEARCHNEXT:
+ case SCI_SEARCHPREV:
+ return SearchText(iMessage, wParam, lParam);
+
+#ifdef INCLUDE_DEPRECATED_FEATURES
+ case SCI_SETCARETPOLICY: // Deprecated
+ caretXPolicy = caretYPolicy = wParam;
+ caretXSlop = caretYSlop = lParam;
+ break;
+#endif
+
+ case SCI_SETXCARETPOLICY:
+ caretXPolicy = wParam;
+ caretXSlop = lParam;
+ break;
+
+ case SCI_SETYCARETPOLICY:
+ caretYPolicy = wParam;
+ caretYSlop = lParam;
+ break;
+
+ case SCI_SETVISIBLEPOLICY:
+ visiblePolicy = wParam;
+ visibleSlop = lParam;
+ break;
+
+ case SCI_LINESONSCREEN:
+ return LinesOnScreen();
+
+ case SCI_SETSELFORE:
+ vs.selforeset = wParam != 0;
+ vs.selforeground.desired = ColourDesired(lParam);
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_SETSELBACK:
+ vs.selbackset = wParam != 0;
+ vs.selbackground.desired = ColourDesired(lParam);
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_SETSELALPHA:
+ vs.selAlpha = wParam;
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_GETSELALPHA:
+ return vs.selAlpha;
+
+ case SCI_SETWHITESPACEFORE:
+ vs.whitespaceForegroundSet = wParam != 0;
+ vs.whitespaceForeground.desired = ColourDesired(lParam);
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_SETWHITESPACEBACK:
+ vs.whitespaceBackgroundSet = wParam != 0;
+ vs.whitespaceBackground.desired = ColourDesired(lParam);
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_SETCARETFORE:
+ vs.caretcolour.desired = ColourDesired(wParam);
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_GETCARETFORE:
+ return vs.caretcolour.desired.AsLong();
+
+ case SCI_SETCARETWIDTH:
+ if (wParam <= 0)
+ vs.caretWidth = 0;
+ else if (wParam >= 3)
+ vs.caretWidth = 3;
+ else
+ vs.caretWidth = wParam;
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_GETCARETWIDTH:
+ return vs.caretWidth;
+
+ case SCI_ASSIGNCMDKEY:
+ kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
+ Platform::HighShortFromLong(wParam), lParam);
+ break;
+
+ case SCI_CLEARCMDKEY:
+ kmap.AssignCmdKey(Platform::LowShortFromLong(wParam),
+ Platform::HighShortFromLong(wParam), SCI_NULL);
+ break;
+
+ case SCI_CLEARALLCMDKEYS:
+ kmap.Clear();
+ break;
+
+ case SCI_INDICSETSTYLE:
+ if (wParam <= INDIC_MAX) {
+ vs.indicators[wParam].style = lParam;
+ InvalidateStyleRedraw();
+ }
+ break;
+
+ case SCI_INDICGETSTYLE:
+ return (wParam <= INDIC_MAX) ? vs.indicators[wParam].style : 0;
+
+ case SCI_INDICSETFORE:
+ if (wParam <= INDIC_MAX) {
+ vs.indicators[wParam].fore.desired = ColourDesired(lParam);
+ InvalidateStyleRedraw();
+ }
+ break;
+
+ case SCI_INDICGETFORE:
+ return (wParam <= INDIC_MAX) ? vs.indicators[wParam].fore.desired.AsLong() : 0;
+
+ case SCI_LINEDOWN:
+ case SCI_LINEDOWNEXTEND:
+ case SCI_PARADOWN:
+ case SCI_PARADOWNEXTEND:
+ case SCI_LINEUP:
+ case SCI_LINEUPEXTEND:
+ case SCI_PARAUP:
+ case SCI_PARAUPEXTEND:
+ case SCI_CHARLEFT:
+ case SCI_CHARLEFTEXTEND:
+ case SCI_CHARRIGHT:
+ case SCI_CHARRIGHTEXTEND:
+ case SCI_WORDLEFT:
+ case SCI_WORDLEFTEXTEND:
+ case SCI_WORDRIGHT:
+ case SCI_WORDRIGHTEXTEND:
+ case SCI_WORDLEFTEND:
+ case SCI_WORDLEFTENDEXTEND:
+ case SCI_WORDRIGHTEND:
+ case SCI_WORDRIGHTENDEXTEND:
+ case SCI_HOME:
+ case SCI_HOMEEXTEND:
+ case SCI_LINEEND:
+ case SCI_LINEENDEXTEND:
+ case SCI_HOMEWRAP:
+ case SCI_HOMEWRAPEXTEND:
+ case SCI_LINEENDWRAP:
+ case SCI_LINEENDWRAPEXTEND:
+ case SCI_DOCUMENTSTART:
+ case SCI_DOCUMENTSTARTEXTEND:
+ case SCI_DOCUMENTEND:
+ case SCI_DOCUMENTENDEXTEND:
+
+ case SCI_STUTTEREDPAGEUP:
+ case SCI_STUTTEREDPAGEUPEXTEND:
+ case SCI_STUTTEREDPAGEDOWN:
+ case SCI_STUTTEREDPAGEDOWNEXTEND:
+
+ case SCI_PAGEUP:
+ case SCI_PAGEUPEXTEND:
+ case SCI_PAGEDOWN:
+ case SCI_PAGEDOWNEXTEND:
+ case SCI_EDITTOGGLEOVERTYPE:
+ case SCI_CANCEL:
+ case SCI_DELETEBACK:
+ case SCI_TAB:
+ case SCI_BACKTAB:
+ case SCI_NEWLINE:
+ case SCI_FORMFEED:
+ case SCI_VCHOME:
+ case SCI_VCHOMEEXTEND:
+ case SCI_VCHOMEWRAP:
+ case SCI_VCHOMEWRAPEXTEND:
+ case SCI_ZOOMIN:
+ case SCI_ZOOMOUT:
+ case SCI_DELWORDLEFT:
+ case SCI_DELWORDRIGHT:
+ case SCI_DELLINELEFT:
+ case SCI_DELLINERIGHT:
+ case SCI_LINECOPY:
+ case SCI_LINECUT:
+ case SCI_LINEDELETE:
+ case SCI_LINETRANSPOSE:
+ case SCI_LINEDUPLICATE:
+ case SCI_LOWERCASE:
+ case SCI_UPPERCASE:
+ case SCI_LINESCROLLDOWN:
+ case SCI_LINESCROLLUP:
+ case SCI_WORDPARTLEFT:
+ case SCI_WORDPARTLEFTEXTEND:
+ case SCI_WORDPARTRIGHT:
+ case SCI_WORDPARTRIGHTEXTEND:
+ case SCI_DELETEBACKNOTLINE:
+ case SCI_HOMEDISPLAY:
+ case SCI_HOMEDISPLAYEXTEND:
+ case SCI_LINEENDDISPLAY:
+ case SCI_LINEENDDISPLAYEXTEND:
+ case SCI_LINEDOWNRECTEXTEND:
+ case SCI_LINEUPRECTEXTEND:
+ case SCI_CHARLEFTRECTEXTEND:
+ case SCI_CHARRIGHTRECTEXTEND:
+ case SCI_HOMERECTEXTEND:
+ case SCI_VCHOMERECTEXTEND:
+ case SCI_LINEENDRECTEXTEND:
+ case SCI_PAGEUPRECTEXTEND:
+ case SCI_PAGEDOWNRECTEXTEND:
+ case SCI_SELECTIONDUPLICATE:
+ return KeyCommand(iMessage);
+
+ case SCI_BRACEHIGHLIGHT:
+ SetBraceHighlight(static_cast<int>(wParam), lParam, STYLE_BRACELIGHT);
+ break;
+
+ case SCI_BRACEBADLIGHT:
+ SetBraceHighlight(static_cast<int>(wParam), -1, STYLE_BRACEBAD);
+ break;
+
+ case SCI_BRACEMATCH:
+ // wParam is position of char to find brace for,
+ // lParam is maximum amount of text to restyle to find it
+ return pdoc->BraceMatch(wParam, lParam);
+
+ case SCI_GETVIEWEOL:
+ return vs.viewEOL;
+
+ case SCI_SETVIEWEOL:
+ vs.viewEOL = wParam != 0;
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_SETZOOM:
+ vs.zoomLevel = wParam;
+ InvalidateStyleRedraw();
+ NotifyZoom();
+ break;
+
+ case SCI_GETZOOM:
+ return vs.zoomLevel;
+
+ case SCI_GETEDGECOLUMN:
+ return theEdge;
+
+ case SCI_SETEDGECOLUMN:
+ theEdge = wParam;
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_GETEDGEMODE:
+ return vs.edgeState;
+
+ case SCI_SETEDGEMODE:
+ vs.edgeState = wParam;
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_GETEDGECOLOUR:
+ return vs.edgecolour.desired.AsLong();
+
+ case SCI_SETEDGECOLOUR:
+ vs.edgecolour.desired = ColourDesired(wParam);
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_GETDOCPOINTER:
+ return reinterpret_cast<sptr_t>(pdoc);
+
+ case SCI_SETDOCPOINTER:
+ CancelModes();
+ SetDocPointer(reinterpret_cast<Document *>(lParam));
+ return 0;
+
+ case SCI_CREATEDOCUMENT: {
+ Document *doc = new Document();
+ if (doc) {
+ doc->AddRef();
+ }
+ return reinterpret_cast<sptr_t>(doc);
+ }
+
+ case SCI_ADDREFDOCUMENT:
+ (reinterpret_cast<Document *>(lParam))->AddRef();
+ break;
+
+ case SCI_RELEASEDOCUMENT:
+ (reinterpret_cast<Document *>(lParam))->Release();
+ break;
+
+ case SCI_SETMODEVENTMASK:
+ modEventMask = wParam;
+ return 0;
+
+ case SCI_GETMODEVENTMASK:
+ return modEventMask;
+
+ case SCI_CONVERTEOLS:
+ pdoc->ConvertLineEnds(wParam);
+ SetSelection(currentPos, anchor); // Ensure selection inside document
+ return 0;
+
+ case SCI_SETLENGTHFORENCODE:
+ lengthForEncode = wParam;
+ return 0;
+
+ case SCI_SELECTIONISRECTANGLE:
+ return selType == selRectangle ? 1 : 0;
+
+ case SCI_SETSELECTIONMODE: {
+ switch (wParam) {
+ case SC_SEL_STREAM:
+ moveExtendsSelection = !moveExtendsSelection || (selType != selStream);
+ selType = selStream;
+ break;
+ case SC_SEL_RECTANGLE:
+ moveExtendsSelection = !moveExtendsSelection || (selType != selRectangle);
+ selType = selRectangle;
+ break;
+ case SC_SEL_LINES:
+ moveExtendsSelection = !moveExtendsSelection || (selType != selLines);
+ selType = selLines;
+ break;
+ default:
+ moveExtendsSelection = !moveExtendsSelection || (selType != selStream);
+ selType = selStream;
+ }
+ InvalidateSelection(currentPos, anchor);
+ }
+ case SCI_GETSELECTIONMODE:
+ switch (selType) {
+ case selStream:
+ return SC_SEL_STREAM;
+ case selRectangle:
+ return SC_SEL_RECTANGLE;
+ case selLines:
+ return SC_SEL_LINES;
+ default: // ?!
+ return SC_SEL_STREAM;
+ }
+ case SCI_GETLINESELSTARTPOSITION: {
+ SelectionLineIterator lineIterator(this);
+ lineIterator.SetAt(wParam);
+ return lineIterator.startPos;
+ }
+ case SCI_GETLINESELENDPOSITION: {
+ SelectionLineIterator lineIterator(this);
+ lineIterator.SetAt(wParam);
+ return lineIterator.endPos;
+ }
+
+ case SCI_SETOVERTYPE:
+ inOverstrike = wParam != 0;
+ break;
+
+ case SCI_GETOVERTYPE:
+ return inOverstrike ? 1 : 0;
+
+ case SCI_SETFOCUS:
+ SetFocusState(wParam != 0);
+ break;
+
+ case SCI_GETFOCUS:
+ return hasFocus;
+
+ case SCI_SETSTATUS:
+ errorStatus = wParam;
+ break;
+
+ case SCI_GETSTATUS:
+ return errorStatus;
+
+ case SCI_SETMOUSEDOWNCAPTURES:
+ mouseDownCaptures = wParam != 0;
+ break;
+
+ case SCI_GETMOUSEDOWNCAPTURES:
+ return mouseDownCaptures;
+
+ case SCI_SETCURSOR:
+ cursorMode = wParam;
+ DisplayCursor(Window::cursorText);
+ break;
+
+ case SCI_GETCURSOR:
+ return cursorMode;
+
+ case SCI_SETCONTROLCHARSYMBOL:
+ controlCharSymbol = wParam;
+ break;
+
+ case SCI_GETCONTROLCHARSYMBOL:
+ return controlCharSymbol;
+
+ case SCI_STARTRECORD:
+ recordingMacro = true;
+ return 0;
+
+ case SCI_STOPRECORD:
+ recordingMacro = false;
+ return 0;
+
+ case SCI_MOVECARETINSIDEVIEW:
+ MoveCaretInsideView();
+ break;
+
+ case SCI_SETFOLDMARGINCOLOUR:
+ vs.foldmarginColourSet = wParam != 0;
+ vs.foldmarginColour.desired = ColourDesired(lParam);
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_SETFOLDMARGINHICOLOUR:
+ vs.foldmarginHighlightColourSet = wParam != 0;
+ vs.foldmarginHighlightColour.desired = ColourDesired(lParam);
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_SETHOTSPOTACTIVEFORE:
+ vs.hotspotForegroundSet = wParam != 0;
+ vs.hotspotForeground.desired = ColourDesired(lParam);
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_SETHOTSPOTACTIVEBACK:
+ vs.hotspotBackgroundSet = wParam != 0;
+ vs.hotspotBackground.desired = ColourDesired(lParam);
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_SETHOTSPOTACTIVEUNDERLINE:
+ vs.hotspotUnderline = wParam != 0;
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_SETHOTSPOTSINGLELINE:
+ vs.hotspotSingleLine = wParam != 0;
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_SETPASTECONVERTENDINGS:
+ convertPastes = wParam != 0;
+ break;
+
+ case SCI_GETPASTECONVERTENDINGS:
+ return convertPastes ? 1 : 0;
+
+ default:
+ return DefWndProc(iMessage, wParam, lParam);
+ }
+ //Platform::DebugPrintf("end wnd proc\n");
+ return 0l;
+}
diff --git a/src/Editor.h b/src/Editor.h
new file mode 100755
index 0000000..fe7be26
--- /dev/null
+++ b/src/Editor.h
@@ -0,0 +1,582 @@
+// Scintilla source code edit control
+/** @file Editor.h
+ ** Defines the main editor class.
+ **/
+// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef EDITOR_H
+#define EDITOR_H
+
+/**
+ */
+class Caret {
+public:
+ bool active;
+ bool on;
+ int period;
+
+ Caret();
+};
+
+/**
+ */
+class Timer {
+public:
+ bool ticking;
+ int ticksToWait;
+ enum {tickSize = 100};
+ TickerID tickerID;
+
+ Timer();
+};
+
+/**
+ */
+class Idler {
+public:
+ bool state;
+ IdlerID idlerID;
+
+ Idler();
+};
+
+/**
+ */
+class LineLayout {
+private:
+ friend class LineLayoutCache;
+ int *lineStarts;
+ int lenLineStarts;
+ /// Drawing is only performed for @a maxLineLength characters on each line.
+ int lineNumber;
+ bool inCache;
+public:
+ enum { wrapWidthInfinite = 0x7ffffff };
+ int maxLineLength;
+ int numCharsInLine;
+ enum validLevel { llInvalid, llCheckTextAndStyle, llPositions, llLines } validity;
+ int xHighlightGuide;
+ bool highlightColumn;
+ int selStart;
+ int selEnd;
+ bool containsCaret;
+ int edgeColumn;
+ char *chars;
+ unsigned char *styles;
+ int styleBitsSet;
+ char *indicators;
+ int *positions;
+ char bracePreviousStyles[2];
+
+ // Hotspot support
+ int hsStart;
+ int hsEnd;
+
+ // Wrapped line support
+ int widthLine;
+ int lines;
+
+ LineLayout(int maxLineLength_);
+ virtual ~LineLayout();
+ void Resize(int maxLineLength_);
+ void Free();
+ void Invalidate(validLevel validity_);
+ int LineStart(int line) {
+ if (line <= 0) {
+ return 0;
+ } else if ((line >= lines) || !lineStarts) {
+ return numCharsInLine;
+ } else {
+ return lineStarts[line];
+ }
+ }
+ void SetLineStart(int line, int start);
+ void SetBracesHighlight(Range rangeLine, Position braces[],
+ char bracesMatchStyle, int xHighlight);
+ void RestoreBracesHighlight(Range rangeLine, Position braces[]);
+};
+
+/**
+ */
+class LineLayoutCache {
+ int level;
+ int length;
+ int size;
+ LineLayout **cache;
+ bool allInvalidated;
+ int styleClock;
+ int useCount;
+ void Allocate(int length_);
+ void AllocateForLevel(int linesOnScreen, int linesInDoc);
+public:
+ LineLayoutCache();
+ virtual ~LineLayoutCache();
+ void Deallocate();
+ enum {
+ llcNone=SC_CACHE_NONE,
+ llcCaret=SC_CACHE_CARET,
+ llcPage=SC_CACHE_PAGE,
+ llcDocument=SC_CACHE_DOCUMENT
+ };
+ void Invalidate(LineLayout::validLevel validity_);
+ void SetLevel(int level_);
+ int GetLevel() { return level; }
+ LineLayout *Retrieve(int lineNumber, int lineCaret, int maxChars, int styleClock_,
+ int linesOnScreen, int linesInDoc);
+ void Dispose(LineLayout *ll);
+};
+
+/**
+ * Hold a piece of text selected for copying or dragging.
+ * The text is expected to hold a terminating '\0' and this is counted in len.
+ */
+class SelectionText {
+public:
+ char *s;
+ int len;
+ bool rectangular;
+ int codePage;
+ int characterSet;
+ SelectionText() : s(0), len(0), rectangular(false), codePage(0), characterSet(0) {}
+ ~SelectionText() {
+ Free();
+ }
+ void Free() {
+ Set(0, 0, 0, 0, false);
+ }
+ void Set(char *s_, int len_, int codePage_, int characterSet_, bool rectangular_) {
+ delete []s;
+ s = s_;
+ if (s)
+ len = len_;
+ else
+ len = 0;
+ codePage = codePage_;
+ characterSet = characterSet_;
+ rectangular = rectangular_;
+ }
+ void Copy(const char *s_, int len_, int codePage_, int characterSet_, bool rectangular_) {
+ delete []s;
+ s = new char[len_];
+ if (s) {
+ len = len_;
+ for (int i = 0; i < len_; i++) {
+ s[i] = s_[i];
+ }
+ } else {
+ len = 0;
+ }
+ codePage = codePage_;
+ characterSet = characterSet_;
+ rectangular = rectangular_;
+ }
+ void Copy(const SelectionText &other) {
+ Copy(other.s, other.len, other.codePage, other.characterSet, other.rectangular);
+ }
+};
+
+/**
+ */
+class Editor : public DocWatcher {
+ // Private so Editor objects can not be copied
+ Editor(const Editor &) : DocWatcher() {}
+ Editor &operator=(const Editor &) { return *this; }
+
+protected: // ScintillaBase subclass needs access to much of Editor
+
+ /** On GTK+, Scintilla is a container widget holding two scroll bars
+ * whereas on Windows there is just one window with both scroll bars turned on. */
+ Window wMain; ///< The Scintilla parent window
+
+ /** Style resources may be expensive to allocate so are cached between uses.
+ * When a style attribute is changed, this cache is flushed. */
+ bool stylesValid;
+ ViewStyle vs;
+ Palette palette;
+
+ int printMagnification;
+ int printColourMode;
+ int printWrapState;
+ int cursorMode;
+ int controlCharSymbol;
+
+ bool hasFocus;
+ bool hideSelection;
+ bool inOverstrike;
+ int errorStatus;
+ bool mouseDownCaptures;
+
+ /** In bufferedDraw mode, graphics operations are drawn to a pixmap and then copied to
+ * the screen. This avoids flashing but is about 30% slower. */
+ bool bufferedDraw;
+ /** In twoPhaseDraw mode, drawing is performed in two phases, first the background
+ * and then the foreground. This avoids chopping off characters that overlap the next run. */
+ bool twoPhaseDraw;
+
+ int xOffset; ///< Horizontal scrolled amount in pixels
+ int xCaretMargin; ///< Ensure this many pixels visible on both sides of caret
+ bool horizontalScrollBarVisible;
+ int scrollWidth;
+ bool verticalScrollBarVisible;
+ bool endAtLastLine;
+ bool caretSticky;
+
+ Surface *pixmapLine;
+ Surface *pixmapSelMargin;
+ Surface *pixmapSelPattern;
+ Surface *pixmapIndentGuide;
+ Surface *pixmapIndentGuideHighlight;
+
+ LineLayoutCache llc;
+
+ KeyMap kmap;
+
+ Caret caret;
+ Timer timer;
+ Timer autoScrollTimer;
+ enum { autoScrollDelay = 200 };
+
+ Idler idler;
+
+ Point lastClick;
+ unsigned int lastClickTime;
+ int dwellDelay;
+ int ticksToDwell;
+ bool dwelling;
+ enum { selChar, selWord, selLine } selectionType;
+ Point ptMouseLast;
+ bool inDragDrop;
+ bool dropWentOutside;
+ int posDrag;
+ int posDrop;
+ int lastXChosen;
+ int lineAnchor;
+ int originalAnchorPos;
+ int currentPos;
+ int anchor;
+ int targetStart;
+ int targetEnd;
+ int searchFlags;
+ int topLine;
+ int posTopLine;
+ int lengthForEncode;
+
+ bool needUpdateUI;
+ Position braces[2];
+ int bracesMatchStyle;
+ int highlightGuideColumn;
+
+ int theEdge;
+
+ enum { notPainting, painting, paintAbandoned } paintState;
+ PRectangle rcPaint;
+ bool paintingAllText;
+
+ int modEventMask;
+
+ SelectionText drag;
+ enum selTypes { noSel, selStream, selRectangle, selLines };
+ selTypes selType;
+ bool moveExtendsSelection;
+ int xStartSelect; ///< x position of start of rectangular selection
+ int xEndSelect; ///< x position of end of rectangular selection
+ bool primarySelection;
+
+ int caretXPolicy;
+ int caretXSlop; ///< Ensure this many pixels visible on both sides of caret
+
+ int caretYPolicy;
+ int caretYSlop; ///< Ensure this many lines visible on both sides of caret
+
+ int visiblePolicy;
+ int visibleSlop;
+
+ int searchAnchor;
+
+ bool recordingMacro;
+
+ int foldFlags;
+ ContractionState cs;
+
+ // Hotspot support
+ int hsStart;
+ int hsEnd;
+
+ // Wrapping support
+ enum { eWrapNone, eWrapWord, eWrapChar } wrapState;
+ enum { wrapLineLarge = 0x7ffffff };
+ int wrapWidth;
+ int wrapStart;
+ int wrapEnd;
+ int wrapVisualFlags;
+ int wrapVisualFlagsLocation;
+ int wrapVisualStartIndent;
+ int actualWrapVisualStartIndent;
+
+ bool convertPastes;
+
+ Document *pdoc;
+
+ Editor();
+ virtual ~Editor();
+ virtual void Initialise() = 0;
+ virtual void Finalise();
+
+ void InvalidateStyleData();
+ void InvalidateStyleRedraw();
+ virtual void RefreshColourPalette(Palette &pal, bool want);
+ void RefreshStyleData();
+ void DropGraphics();
+
+ virtual PRectangle GetClientRectangle();
+ PRectangle GetTextRectangle();
+
+ int LinesOnScreen();
+ int LinesToScroll();
+ int MaxScrollPos();
+ Point LocationFromPosition(int pos);
+ int XFromPosition(int pos);
+ int PositionFromLocation(Point pt);
+ int PositionFromLocationClose(Point pt);
+ int PositionFromLineX(int line, int x);
+ int LineFromLocation(Point pt);
+ void SetTopLine(int topLineNew);
+
+ bool AbandonPaint();
+ void RedrawRect(PRectangle rc);
+ void Redraw();
+ void RedrawSelMargin(int line=-1);
+ PRectangle RectangleFromRange(int start, int end);
+ void InvalidateRange(int start, int end);
+
+ int CurrentPosition();
+ bool SelectionEmpty();
+ int SelectionStart();
+ int SelectionEnd();
+ void SetRectangularRange();
+ void InvalidateSelection(int currentPos_, int anchor_);
+ void SetSelection(int currentPos_, int anchor_);
+ void SetSelection(int currentPos_);
+ void SetEmptySelection(int currentPos_);
+ bool RangeContainsProtected(int start, int end) const;
+ bool SelectionContainsProtected();
+ int MovePositionOutsideChar(int pos, int moveDir, bool checkLineEnd=true);
+ int MovePositionTo(int newPos, selTypes sel=noSel, bool ensureVisible=true);
+ int MovePositionSoVisible(int pos, int moveDir);
+ void SetLastXChosen();
+
+ void ScrollTo(int line, bool moveThumb=true);
+ virtual void ScrollText(int linesToMove);
+ void HorizontalScrollTo(int xPos);
+ void MoveCaretInsideView(bool ensureVisible=true);
+ int DisplayFromPosition(int pos);
+ void EnsureCaretVisible(bool useMargin=true, bool vert=true, bool horiz=true);
+ void ShowCaretAtCurrentPosition();
+ void DropCaret();
+ void InvalidateCaret();
+ virtual void UpdateSystemCaret();
+
+ void NeedWrapping(int docLineStart = 0, int docLineEnd = wrapLineLarge);
+ bool WrapLines(bool fullWrap, int priorityWrapLineStart);
+ void LinesJoin();
+ void LinesSplit(int pixelWidth);
+
+ int SubstituteMarkerIfEmpty(int markerCheck, int markerDefault);
+ void PaintSelMargin(Surface *surface, PRectangle &rc);
+ LineLayout *RetrieveLineLayout(int lineNumber);
+ void LayoutLine(int line, Surface *surface, ViewStyle &vstyle, LineLayout *ll,
+ int width=LineLayout::wrapWidthInfinite);
+ ColourAllocated SelectionBackground(ViewStyle &vsDraw);
+ ColourAllocated TextBackground(ViewStyle &vsDraw, bool overrideBackground, ColourAllocated background, bool inSelection, bool inHotspot, int styleMain, int i, LineLayout *ll);
+ void DrawIndentGuide(Surface *surface, int lineVisible, int lineHeight, int start, PRectangle rcSegment, bool highlight);
+ void DrawWrapMarker(Surface *surface, PRectangle rcPlace, bool isEndMarker, ColourAllocated wrapColour);
+ void DrawEOL(Surface *surface, ViewStyle &vsDraw, PRectangle rcLine, LineLayout *ll,
+ int line, int lineEnd, int xStart, int subLine, int subLineStart,
+ bool overrideBackground, ColourAllocated background,
+ bool drawWrapMark, ColourAllocated wrapColour);
+ void DrawLine(Surface *surface, ViewStyle &vsDraw, int line, int lineVisible, int xStart,
+ PRectangle rcLine, LineLayout *ll, int subLine=0);
+ void RefreshPixMaps(Surface *surfaceWindow);
+ void Paint(Surface *surfaceWindow, PRectangle rcArea);
+ long FormatRange(bool draw, RangeToFormat *pfr);
+ int TextWidth(int style, const char *text);
+
+ virtual void SetVerticalScrollPos() = 0;
+ virtual void SetHorizontalScrollPos() = 0;
+ virtual bool ModifyScrollBars(int nMax, int nPage) = 0;
+ virtual void ReconfigureScrollBars();
+ void SetScrollBars();
+ void ChangeSize();
+
+ void AddChar(char ch);
+ virtual void AddCharUTF(char *s, unsigned int len, bool treatAsDBCS=false);
+ void ClearSelection();
+ void ClearAll();
+ void ClearDocumentStyle();
+ void Cut();
+ void PasteRectangular(int pos, const char *ptr, int len);
+ virtual void Copy() = 0;
+ virtual bool CanPaste();
+ virtual void Paste() = 0;
+ void Clear();
+ void SelectAll();
+ void Undo();
+ void Redo();
+ void DelChar();
+ void DelCharBack(bool allowLineStartDeletion);
+ virtual void ClaimSelection() = 0;
+
+ virtual void NotifyChange() = 0;
+ virtual void NotifyFocus(bool focus);
+ virtual int GetCtrlID() { return ctrlID; }
+ virtual void NotifyParent(SCNotification scn) = 0;
+ virtual void NotifyStyleToNeeded(int endStyleNeeded);
+ void NotifyChar(int ch);
+ void NotifyMove(int position);
+ void NotifySavePoint(bool isSavePoint);
+ void NotifyModifyAttempt();
+ virtual void NotifyDoubleClick(Point pt, bool shift);
+ void NotifyHotSpotClicked(int position, bool shift, bool ctrl, bool alt);
+ void NotifyHotSpotDoubleClicked(int position, bool shift, bool ctrl, bool alt);
+ void NotifyUpdateUI();
+ void NotifyPainted();
+ bool NotifyMarginClick(Point pt, bool shift, bool ctrl, bool alt);
+ void NotifyNeedShown(int pos, int len);
+ void NotifyDwelling(Point pt, bool state);
+ void NotifyZoom();
+
+ void NotifyModifyAttempt(Document *document, void *userData);
+ void NotifySavePoint(Document *document, void *userData, bool atSavePoint);
+ void CheckModificationForWrap(DocModification mh);
+ void NotifyModified(Document *document, DocModification mh, void *userData);
+ void NotifyDeleted(Document *document, void *userData);
+ void NotifyStyleNeeded(Document *doc, void *userData, int endPos);
+ void NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
+
+ void PageMove(int direction, selTypes sel=noSel, bool stuttered = false);
+ void ChangeCaseOfSelection(bool makeUpperCase);
+ void LineTranspose();
+ void Duplicate(bool forLine);
+ virtual void CancelModes();
+ void NewLine();
+ void CursorUpOrDown(int direction, selTypes sel=noSel);
+ void ParaUpOrDown(int direction, selTypes sel=noSel);
+ int StartEndDisplayLine(int pos, bool start);
+ virtual int KeyCommand(unsigned int iMessage);
+ virtual int KeyDefault(int /* key */, int /*modifiers*/);
+ int KeyDown(int key, bool shift, bool ctrl, bool alt, bool *consumed=0);
+
+ int GetWhitespaceVisible();
+ void SetWhitespaceVisible(int view);
+
+ void Indent(bool forwards);
+
+ long FindText(uptr_t wParam, sptr_t lParam);
+ void SearchAnchor();
+ long SearchText(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
+ long SearchInTarget(const char *text, int length);
+ void GoToLine(int lineNo);
+
+ virtual void CopyToClipboard(const SelectionText &selectedText) = 0;
+ char *CopyRange(int start, int end);
+ void CopySelectionFromRange(SelectionText *ss, int start, int end);
+ void CopySelectionRange(SelectionText *ss);
+ void CopyRangeToClipboard(int start, int end);
+ void CopyText(int length, const char *text);
+ void SetDragPosition(int newPos);
+ virtual void DisplayCursor(Window::Cursor c);
+ virtual void StartDrag();
+ void DropAt(int position, const char *value, bool moving, bool rectangular);
+ /** PositionInSelection returns 0 if position in selection, -1 if position before selection, and 1 if after.
+ * Before means either before any line of selection or before selection on its line, with a similar meaning to after. */
+ int PositionInSelection(int pos);
+ bool PointInSelection(Point pt);
+ bool PointInSelMargin(Point pt);
+ void LineSelection(int lineCurrent_, int lineAnchor_);
+ void DwellEnd(bool mouseMoved);
+ virtual void ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt);
+ void ButtonMove(Point pt);
+ void ButtonUp(Point pt, unsigned int curTime, bool ctrl);
+
+ void Tick();
+ bool Idle();
+ virtual void SetTicking(bool on) = 0;
+ virtual bool SetIdle(bool) { return false; }
+ virtual void SetMouseCapture(bool on) = 0;
+ virtual bool HaveMouseCapture() = 0;
+ void SetFocusState(bool focusState);
+
+ virtual bool PaintContains(PRectangle rc);
+ bool PaintContainsMargin();
+ void CheckForChangeOutsidePaint(Range r);
+ void SetBraceHighlight(Position pos0, Position pos1, int matchStyle);
+
+ void SetDocPointer(Document *document);
+
+ void Expand(int &line, bool doExpand);
+ void ToggleContraction(int line);
+ void EnsureLineVisible(int lineDoc, bool enforcePolicy);
+ int ReplaceTarget(bool replacePatterns, const char *text, int length=-1);
+
+ bool PositionIsHotspot(int position);
+ bool PointIsHotspot(Point pt);
+ void SetHotSpotRange(Point *pt);
+ void GetHotSpotRange(int& hsStart, int& hsEnd);
+
+ int CodePage() const;
+ virtual bool ValidCodePage(int /* codePage */) const { return true; }
+ int WrapCount(int line);
+
+ virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) = 0;
+
+public:
+ // Public so the COM thunks can access it.
+ bool IsUnicodeMode() const;
+ // Public so scintilla_send_message can use it.
+ virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
+ // Public so scintilla_set_id can use it.
+ int ctrlID;
+ friend class AutoSurface;
+ friend class SelectionLineIterator;
+};
+
+/**
+ * A smart pointer class to ensure Surfaces are set up and deleted correctly.
+ */
+class AutoSurface {
+private:
+ Surface *surf;
+public:
+ AutoSurface(Editor *ed) : surf(0) {
+ if (ed->wMain.GetID()) {
+ surf = Surface::Allocate();
+ if (surf) {
+ surf->Init(ed->wMain.GetID());
+ surf->SetUnicodeMode(SC_CP_UTF8 == ed->CodePage());
+ surf->SetDBCSMode(ed->CodePage());
+ }
+ }
+ }
+ AutoSurface(SurfaceID sid, Editor *ed) : surf(0) {
+ if (ed->wMain.GetID()) {
+ surf = Surface::Allocate();
+ if (surf) {
+ surf->Init(sid, ed->wMain.GetID());
+ surf->SetUnicodeMode(SC_CP_UTF8 == ed->CodePage());
+ surf->SetDBCSMode(ed->CodePage());
+ }
+ }
+ }
+ ~AutoSurface() {
+ delete surf;
+ }
+ Surface *operator->() const {
+ return surf;
+ }
+ operator Surface *() const {
+ return surf;
+ }
+};
+
+#endif
diff --git a/src/ExternalLexer.cpp b/src/ExternalLexer.cpp
new file mode 100755
index 0000000..acf45bc
--- /dev/null
+++ b/src/ExternalLexer.cpp
@@ -0,0 +1,259 @@
+// Scintilla source code edit control
+/** @file ExternalLexer.cxx
+ ** Support external lexers in DLLs.
+ **/
+// Copyright 2001 Simon Steele <ss@pnotepad.org>, portions copyright Neil Hodgson.
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "Platform.h"
+
+#include "Scintilla.h"
+
+#include "SciLexer.h"
+#include "PropSet.h"
+#include "Accessor.h"
+#include "DocumentAccessor.h"
+#include "KeyWords.h"
+#include "ExternalLexer.h"
+
+LexerManager *LexerManager::theInstance = NULL;
+
+//------------------------------------------
+//
+// ExternalLexerModule
+//
+//------------------------------------------
+
+char **WordListsToStrings(WordList *val[]) {
+ int dim = 0;
+ while (val[dim])
+ dim++;
+ char **wls = new char * [dim + 1];
+ for (int i = 0;i < dim;i++) {
+ SString words;
+ words = "";
+ for (int n = 0; n < val[i]->len; n++) {
+ words += val[i]->words[n];
+ if (n != val[i]->len - 1)
+ words += " ";
+ }
+ wls[i] = new char[words.length() + 1];
+ strcpy(wls[i], words.c_str());
+ }
+ wls[dim] = 0;
+ return wls;
+}
+
+void DeleteWLStrings(char *strs[]) {
+ int dim = 0;
+ while (strs[dim]) {
+ delete strs[dim];
+ dim++;
+ }
+ delete [] strs;
+}
+
+void ExternalLexerModule::Lex(unsigned int startPos, int lengthDoc, int initStyle,
+ WordList *keywordlists[], Accessor &styler) const {
+ if (!fneLexer)
+ return ;
+
+ char **kwds = WordListsToStrings(keywordlists);
+ char *ps = styler.GetProperties();
+
+ // The accessor passed in is always a DocumentAccessor so this cast and the subsequent
+ // access will work. Can not use the stricter dynamic_cast as that requires RTTI.
+ DocumentAccessor &da = static_cast<DocumentAccessor &>(styler);
+ WindowID wID = da.GetWindow();
+
+ fneLexer(externalLanguage, startPos, lengthDoc, initStyle, kwds, wID, ps);
+
+ delete ps;
+ DeleteWLStrings(kwds);
+}
+
+void ExternalLexerModule::Fold(unsigned int startPos, int lengthDoc, int initStyle,
+ WordList *keywordlists[], Accessor &styler) const {
+ if (!fneFolder)
+ return ;
+
+ char **kwds = WordListsToStrings(keywordlists);
+ char *ps = styler.GetProperties();
+
+ // The accessor passed in is always a DocumentAccessor so this cast and the subsequent
+ // access will work. Can not use the stricter dynamic_cast as that requires RTTI.
+ DocumentAccessor &da = static_cast<DocumentAccessor &>(styler);
+ WindowID wID = da.GetWindow();
+
+ fneFolder(externalLanguage, startPos, lengthDoc, initStyle, kwds, wID, ps);
+
+ delete ps;
+ DeleteWLStrings(kwds);
+}
+
+void ExternalLexerModule::SetExternal(ExtLexerFunction fLexer, ExtFoldFunction fFolder, int index) {
+ fneLexer = fLexer;
+ fneFolder = fFolder;
+ externalLanguage = index;
+}
+
+//------------------------------------------
+//
+// LexerLibrary
+//
+//------------------------------------------
+
+LexerLibrary::LexerLibrary(const char* ModuleName) {
+ // Initialise some members...
+ first = NULL;
+ last = NULL;
+
+ // Load the DLL
+ lib = DynamicLibrary::Load(ModuleName);
+ if (lib->IsValid()) {
+ m_sModuleName = ModuleName;
+ //Cannot use reinterpret_cast because: ANSI C++ forbids casting between pointers to functions and objects
+ GetLexerCountFn GetLexerCount = (GetLexerCountFn)(sptr_t)lib->FindFunction("GetLexerCount");
+
+ if (GetLexerCount) {
+ ExternalLexerModule *lex;
+ LexerMinder *lm;
+
+ // Find functions in the DLL
+ GetLexerNameFn GetLexerName = (GetLexerNameFn)(sptr_t)lib->FindFunction("GetLexerName");
+ ExtLexerFunction Lexer = (ExtLexerFunction)(sptr_t)lib->FindFunction("Lex");
+ ExtFoldFunction Folder = (ExtFoldFunction)(sptr_t)lib->FindFunction("Fold");
+
+ // Assign a buffer for the lexer name.
+ char lexname[100];
+ strcpy(lexname, "");
+
+ int nl = GetLexerCount();
+
+ for (int i = 0; i < nl; i++) {
+ GetLexerName(i, lexname, 100);
+ lex = new ExternalLexerModule(SCLEX_AUTOMATIC, NULL, lexname, NULL);
+
+ // Create a LexerMinder so we don't leak the ExternalLexerModule...
+ lm = new LexerMinder;
+ lm->self = lex;
+ lm->next = NULL;
+ if (first != NULL) {
+ last->next = lm;
+ last = lm;
+ } else {
+ first = lm;
+ last = lm;
+ }
+
+ // The external lexer needs to know how to call into its DLL to
+ // do its lexing and folding, we tell it here. Folder may be null.
+ lex->SetExternal(Lexer, Folder, i);
+ }
+ }
+ }
+ next = NULL;
+}
+
+LexerLibrary::~LexerLibrary() {
+ Release();
+ delete lib;
+}
+
+void LexerLibrary::Release() {
+ //TODO maintain a list of lexers created, and delete them!
+ LexerMinder *lm;
+ LexerMinder *next;
+ lm = first;
+ while (NULL != lm) {
+ next = lm->next;
+ delete lm->self;
+ delete lm;
+ lm = next;
+ }
+
+ first = NULL;
+ last = NULL;
+}
+
+//------------------------------------------
+//
+// LexerManager
+//
+//------------------------------------------
+
+/// Return the single LexerManager instance...
+LexerManager *LexerManager::GetInstance() {
+ if(!theInstance)
+ theInstance = new LexerManager;
+ return theInstance;
+}
+
+/// Delete any LexerManager instance...
+void LexerManager::DeleteInstance()
+{
+ if(theInstance) {
+ delete theInstance;
+ theInstance = NULL;
+ }
+}
+
+/// protected constructor - this is a singleton...
+LexerManager::LexerManager() {
+ first = NULL;
+ last = NULL;
+}
+
+LexerManager::~LexerManager() {
+ Clear();
+}
+
+void LexerManager::Load(const char* path)
+{
+ LoadLexerLibrary(path);
+}
+
+void LexerManager::LoadLexerLibrary(const char* module)
+{
+ LexerLibrary *lib = new LexerLibrary(module);
+ if (NULL != first) {
+ last->next = lib;
+ last = lib;
+ } else {
+ first = lib;
+ last = lib;
+ }
+}
+
+void LexerManager::Clear()
+{
+ if (NULL != first) {
+ LexerLibrary *cur = first;
+ LexerLibrary *next;
+ while (cur) {
+ next = cur->next;
+ delete cur;
+ cur = next;
+ }
+ first = NULL;
+ last = NULL;
+ }
+}
+
+//------------------------------------------
+//
+// LexerManager
+//
+//------------------------------------------
+
+LMMinder::~LMMinder()
+{
+ LexerManager::DeleteInstance();
+}
+
+LMMinder minder;
diff --git a/src/ExternalLexer.h b/src/ExternalLexer.h
new file mode 100644
index 0000000..fe9da36
--- /dev/null
+++ b/src/ExternalLexer.h
@@ -0,0 +1,102 @@
+// Scintilla source code edit control
+/** @file ExternalLexer.h
+ ** Support external lexers in DLLs.
+ **/
+// Copyright 2001 Simon Steele <ss@pnotepad.org>, portions copyright Neil Hodgson.
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef EXTERNALLEXER_H
+#define EXTERNALLEXER_H
+
+#if PLAT_WIN
+#define EXT_LEXER_DECL __stdcall
+#elif PLAT_GTK
+#define EXT_LEXER_DECL
+#elif PLAT_QT
+#include <qglobal.h>
+#if defined(Q_OS_WIN32) || defined(Q_OS_WIN64)
+#define EXT_LEXER_DECL __stdcall
+#else
+#define EXT_LEXER_DECL
+#endif
+#endif
+
+// External Lexer function definitions...
+typedef void (EXT_LEXER_DECL *ExtLexerFunction)(unsigned int lexer, unsigned int startPos, int length, int initStyle,
+ char *words[], WindowID window, char *props);
+typedef void (EXT_LEXER_DECL *ExtFoldFunction)(unsigned int lexer, unsigned int startPos, int length, int initStyle,
+ char *words[], WindowID window, char *props);
+typedef void* (EXT_LEXER_DECL *GetLexerFunction)(unsigned int Index);
+typedef int (EXT_LEXER_DECL *GetLexerCountFn)();
+typedef void (EXT_LEXER_DECL *GetLexerNameFn)(unsigned int Index, char *name, int buflength);
+
+//class DynamicLibrary;
+
+/// Sub-class of LexerModule to use an external lexer.
+class ExternalLexerModule : protected LexerModule {
+protected:
+ ExtLexerFunction fneLexer;
+ ExtFoldFunction fneFolder;
+ int externalLanguage;
+ char name[100];
+public:
+ ExternalLexerModule(int language_, LexerFunction fnLexer_,
+ const char *languageName_=0, LexerFunction fnFolder_=0) : LexerModule(language_, fnLexer_, 0, fnFolder_){
+ strncpy(name, languageName_, sizeof(name));
+ languageName = name;
+ };
+ virtual void Lex(unsigned int startPos, int lengthDoc, int initStyle,
+ WordList *keywordlists[], Accessor &styler) const;
+ virtual void Fold(unsigned int startPos, int lengthDoc, int initStyle,
+ WordList *keywordlists[], Accessor &styler) const;
+ virtual void SetExternal(ExtLexerFunction fLexer, ExtFoldFunction fFolder, int index);
+};
+
+/// LexerMinder points to an ExternalLexerModule - so we don't leak them.
+class LexerMinder {
+public:
+ ExternalLexerModule *self;
+ LexerMinder *next;
+};
+
+/// LexerLibrary exists for every External Lexer DLL, contains LexerMinders.
+class LexerLibrary {
+ DynamicLibrary *lib;
+ LexerMinder *first;
+ LexerMinder *last;
+
+public:
+ LexerLibrary(const char* ModuleName);
+ ~LexerLibrary();
+ void Release();
+
+ LexerLibrary *next;
+ SString m_sModuleName;
+};
+
+/// LexerManager manages external lexers, contains LexerLibrarys.
+class LexerManager {
+public:
+ ~LexerManager();
+
+ static LexerManager *GetInstance();
+ static void DeleteInstance();
+
+ void Load(const char* path);
+ void Clear();
+
+private:
+ LexerManager();
+ static LexerManager *theInstance;
+
+ void LoadLexerLibrary(const char* module);
+ LexerLibrary *first;
+ LexerLibrary *last;
+};
+
+class LMMinder {
+public:
+ ~LMMinder();
+};
+
+#endif
diff --git a/src/Indicator.cpp b/src/Indicator.cpp
new file mode 100755
index 0000000..7624a4a
--- /dev/null
+++ b/src/Indicator.cpp
@@ -0,0 +1,77 @@
+// Scintilla source code edit control
+/** @file Indicator.cxx
+ ** Defines the style of indicators which are text decorations such as underlining.
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include "Platform.h"
+
+#include "Scintilla.h"
+#include "Indicator.h"
+
+void Indicator::Draw(Surface *surface, const PRectangle &rc, const PRectangle &rcLine) {
+ surface->PenColour(fore.allocated);
+ int ymid = (rc.bottom + rc.top) / 2;
+ if (style == INDIC_SQUIGGLE) {
+ surface->MoveTo(rc.left, rc.top);
+ int x = rc.left + 2;
+ int y = 2;
+ while (x < rc.right) {
+ surface->LineTo(x, rc.top + y);
+ x += 2;
+ y = 2 - y;
+ }
+ surface->LineTo(rc.right, rc.top + y); // Finish the line
+ } else if (style == INDIC_TT) {
+ surface->MoveTo(rc.left, ymid);
+ int x = rc.left + 5;
+ while (x < rc.right) {
+ surface->LineTo(x, ymid);
+ surface->MoveTo(x-3, ymid);
+ surface->LineTo(x-3, ymid+2);
+ x++;
+ surface->MoveTo(x, ymid);
+ x += 5;
+ }
+ surface->LineTo(rc.right, ymid); // Finish the line
+ if (x - 3 <= rc.right) {
+ surface->MoveTo(x-3, ymid);
+ surface->LineTo(x-3, ymid+2);
+ }
+ } else if (style == INDIC_DIAGONAL) {
+ int x = rc.left;
+ while (x < rc.right) {
+ surface->MoveTo(x, rc.top+2);
+ int endX = x+3;
+ int endY = rc.top - 1;
+ if (endX > rc.right) {
+ endY += endX - rc.right;
+ endX = rc.right;
+ }
+ surface->LineTo(endX, endY);
+ x += 4;
+ }
+ } else if (style == INDIC_STRIKE) {
+ surface->MoveTo(rc.left, rc.top - 4);
+ surface->LineTo(rc.right, rc.top - 4);
+ } else if (style == INDIC_HIDDEN) {
+ // Draw nothing
+ } else if (style == INDIC_BOX) {
+ surface->MoveTo(rc.left, ymid+1);
+ surface->LineTo(rc.right, ymid+1);
+ surface->LineTo(rc.right, rcLine.top+1);
+ surface->LineTo(rc.left, rcLine.top+1);
+ surface->LineTo(rc.left, ymid+1);
+ } else if (style == INDIC_ROUNDBOX) {
+ PRectangle rcBox = rcLine;
+ rcBox.top = rcLine.top + 1;
+ rcBox.left = rc.left;
+ rcBox.right = rc.right;
+ surface->AlphaRectangle(rcBox, 1, fore.allocated, 30, fore.allocated, 50, 0);
+ } else { // Either INDIC_PLAIN or unknown
+ surface->MoveTo(rc.left, ymid);
+ surface->LineTo(rc.right, ymid);
+ }
+}
+
diff --git a/src/Indicator.h b/src/Indicator.h
new file mode 100755
index 0000000..716db10
--- /dev/null
+++ b/src/Indicator.h
@@ -0,0 +1,22 @@
+// Scintilla source code edit control
+/** @file Indicator.h
+ ** Defines the style of indicators which are text decorations such as underlining.
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef INDICATOR_H
+#define INDICATOR_H
+
+/**
+ */
+class Indicator {
+public:
+ int style;
+ ColourPair fore;
+ Indicator() : style(INDIC_PLAIN), fore(ColourDesired(0,0,0)) {
+ }
+ void Draw(Surface *surface, const PRectangle &rc, const PRectangle &rcLine);
+};
+
+#endif
diff --git a/src/KeyMap.cpp b/src/KeyMap.cpp
new file mode 100755
index 0000000..bfa6e2d
--- /dev/null
+++ b/src/KeyMap.cpp
@@ -0,0 +1,148 @@
+// Scintilla source code edit control
+/** @file KeyMap.cxx
+ ** Defines a mapping between keystrokes and commands.
+ **/
+// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include "Platform.h"
+
+#include "Scintilla.h"
+
+#include "KeyMap.h"
+
+KeyMap::KeyMap() : kmap(0), len(0), alloc(0) {
+ for (int i = 0; MapDefault[i].key; i++) {
+ AssignCmdKey(MapDefault[i].key,
+ MapDefault[i].modifiers,
+ MapDefault[i].msg);
+ }
+}
+
+KeyMap::~KeyMap() {
+ Clear();
+}
+
+void KeyMap::Clear() {
+ delete []kmap;
+ kmap = 0;
+ len = 0;
+ alloc = 0;
+}
+
+void KeyMap::AssignCmdKey(int key, int modifiers, unsigned int msg) {
+ if ((len+1) >= alloc) {
+ KeyToCommand *ktcNew = new KeyToCommand[alloc + 5];
+ if (!ktcNew)
+ return;
+ for (int k = 0; k < len; k++)
+ ktcNew[k] = kmap[k];
+ alloc += 5;
+ delete []kmap;
+ kmap = ktcNew;
+ }
+ for (int keyIndex = 0; keyIndex < len; keyIndex++) {
+ if ((key == kmap[keyIndex].key) && (modifiers == kmap[keyIndex].modifiers)) {
+ kmap[keyIndex].msg = msg;
+ return;
+ }
+ }
+ kmap[len].key = key;
+ kmap[len].modifiers = modifiers;
+ kmap[len].msg = msg;
+ len++;
+}
+
+unsigned int KeyMap::Find(int key, int modifiers) {
+ for (int i = 0; i < len; i++) {
+ if ((key == kmap[i].key) && (modifiers == kmap[i].modifiers)) {
+ return kmap[i].msg;
+ }
+ }
+ return 0;
+}
+
+const KeyToCommand KeyMap::MapDefault[] = {
+ {SCK_DOWN, SCI_NORM, SCI_LINEDOWN},
+ {SCK_DOWN, SCI_SHIFT, SCI_LINEDOWNEXTEND},
+ {SCK_DOWN, SCI_CTRL, SCI_LINESCROLLDOWN},
+ {SCK_DOWN, SCI_ASHIFT, SCI_LINEDOWNRECTEXTEND},
+ {SCK_UP, SCI_NORM, SCI_LINEUP},
+ {SCK_UP, SCI_SHIFT, SCI_LINEUPEXTEND},
+ {SCK_UP, SCI_CTRL, SCI_LINESCROLLUP},
+ {SCK_UP, SCI_ASHIFT, SCI_LINEUPRECTEXTEND},
+ {'[', SCI_CTRL, SCI_PARAUP},
+ {'[', SCI_CSHIFT, SCI_PARAUPEXTEND},
+ {']', SCI_CTRL, SCI_PARADOWN},
+ {']', SCI_CSHIFT, SCI_PARADOWNEXTEND},
+ {SCK_LEFT, SCI_NORM, SCI_CHARLEFT},
+ {SCK_LEFT, SCI_SHIFT, SCI_CHARLEFTEXTEND},
+ {SCK_LEFT, SCI_CTRL, SCI_WORDLEFT},
+ {SCK_LEFT, SCI_CSHIFT, SCI_WORDLEFTEXTEND},
+ {SCK_LEFT, SCI_ASHIFT, SCI_CHARLEFTRECTEXTEND},
+ {SCK_RIGHT, SCI_NORM, SCI_CHARRIGHT},
+ {SCK_RIGHT, SCI_SHIFT, SCI_CHARRIGHTEXTEND},
+ {SCK_RIGHT, SCI_CTRL, SCI_WORDRIGHT},
+ {SCK_RIGHT, SCI_CSHIFT, SCI_WORDRIGHTEXTEND},
+ {SCK_RIGHT, SCI_ASHIFT, SCI_CHARRIGHTRECTEXTEND},
+ {'/', SCI_CTRL, SCI_WORDPARTLEFT},
+ {'/', SCI_CSHIFT, SCI_WORDPARTLEFTEXTEND},
+ {'\\', SCI_CTRL, SCI_WORDPARTRIGHT},
+ {'\\', SCI_CSHIFT, SCI_WORDPARTRIGHTEXTEND},
+ {SCK_HOME, SCI_NORM, SCI_VCHOME},
+ {SCK_HOME, SCI_SHIFT, SCI_VCHOMEEXTEND},
+ {SCK_HOME, SCI_CTRL, SCI_DOCUMENTSTART},
+ {SCK_HOME, SCI_CSHIFT, SCI_DOCUMENTSTARTEXTEND},
+ {SCK_HOME, SCI_ALT, SCI_HOMEDISPLAY},
+// {SCK_HOME, SCI_ASHIFT, SCI_HOMEDISPLAYEXTEND},
+ {SCK_HOME, SCI_ASHIFT, SCI_VCHOMERECTEXTEND},
+ {SCK_END, SCI_NORM, SCI_LINEEND},
+ {SCK_END, SCI_SHIFT, SCI_LINEENDEXTEND},
+ {SCK_END, SCI_CTRL, SCI_DOCUMENTEND},
+ {SCK_END, SCI_CSHIFT, SCI_DOCUMENTENDEXTEND},
+ {SCK_END, SCI_ALT, SCI_LINEENDDISPLAY},
+// {SCK_END, SCI_ASHIFT, SCI_LINEENDDISPLAYEXTEND},
+ {SCK_END, SCI_ASHIFT, SCI_LINEENDRECTEXTEND},
+ {SCK_PRIOR, SCI_NORM, SCI_PAGEUP},
+ {SCK_PRIOR, SCI_SHIFT, SCI_PAGEUPEXTEND},
+ {SCK_PRIOR, SCI_ASHIFT, SCI_PAGEUPRECTEXTEND},
+ {SCK_NEXT, SCI_NORM, SCI_PAGEDOWN},
+ {SCK_NEXT, SCI_SHIFT, SCI_PAGEDOWNEXTEND},
+ {SCK_NEXT, SCI_ASHIFT, SCI_PAGEDOWNRECTEXTEND},
+ {SCK_DELETE, SCI_NORM, SCI_CLEAR},
+ {SCK_DELETE, SCI_SHIFT, SCI_CUT},
+ {SCK_DELETE, SCI_CTRL, SCI_DELWORDRIGHT},
+ {SCK_DELETE, SCI_CSHIFT, SCI_DELLINERIGHT},
+ {SCK_INSERT, SCI_NORM, SCI_EDITTOGGLEOVERTYPE},
+ {SCK_INSERT, SCI_SHIFT, SCI_PASTE},
+ {SCK_INSERT, SCI_CTRL, SCI_COPY},
+ {SCK_ESCAPE, SCI_NORM, SCI_CANCEL},
+ {SCK_BACK, SCI_NORM, SCI_DELETEBACK},
+ {SCK_BACK, SCI_SHIFT, SCI_DELETEBACK},
+ {SCK_BACK, SCI_CTRL, SCI_DELWORDLEFT},
+ {SCK_BACK, SCI_ALT, SCI_UNDO},
+ {SCK_BACK, SCI_CSHIFT, SCI_DELLINELEFT},
+ {'Z', SCI_CTRL, SCI_UNDO},
+ {'Y', SCI_CTRL, SCI_REDO},
+ {'X', SCI_CTRL, SCI_CUT},
+ {'C', SCI_CTRL, SCI_COPY},
+ {'V', SCI_CTRL, SCI_PASTE},
+ {'A', SCI_CTRL, SCI_SELECTALL},
+ {SCK_TAB, SCI_NORM, SCI_TAB},
+ {SCK_TAB, SCI_SHIFT, SCI_BACKTAB},
+ {SCK_RETURN, SCI_NORM, SCI_NEWLINE},
+ {SCK_RETURN, SCI_SHIFT, SCI_NEWLINE},
+ {SCK_ADD, SCI_CTRL, SCI_ZOOMIN},
+ {SCK_SUBTRACT, SCI_CTRL, SCI_ZOOMOUT},
+ {SCK_DIVIDE, SCI_CTRL, SCI_SETZOOM},
+ //'L', SCI_CTRL, SCI_FORMFEED,
+ {'L', SCI_CTRL, SCI_LINECUT},
+ {'L', SCI_CSHIFT, SCI_LINEDELETE},
+ {'T', SCI_CSHIFT, SCI_LINECOPY},
+ {'T', SCI_CTRL, SCI_LINETRANSPOSE},
+ {'D', SCI_CTRL, SCI_SELECTIONDUPLICATE},
+ {'U', SCI_CTRL, SCI_LOWERCASE},
+ {'U', SCI_CSHIFT, SCI_UPPERCASE},
+ {0,0,0},
+};
+
diff --git a/src/KeyMap.h b/src/KeyMap.h
new file mode 100755
index 0000000..364df68
--- /dev/null
+++ b/src/KeyMap.h
@@ -0,0 +1,43 @@
+// Scintilla source code edit control
+/** @file KeyMap.h
+ ** Defines a mapping between keystrokes and commands.
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef KEYTOCOMMAND_H
+#define KEYTOCOMMAND_H
+
+#define SCI_NORM 0
+#define SCI_SHIFT SCMOD_SHIFT
+#define SCI_CTRL SCMOD_CTRL
+#define SCI_ALT SCMOD_ALT
+#define SCI_CSHIFT (SCI_CTRL | SCI_SHIFT)
+#define SCI_ASHIFT (SCI_ALT | SCI_SHIFT)
+
+/**
+ */
+class KeyToCommand {
+public:
+ int key;
+ int modifiers;
+ unsigned int msg;
+};
+
+/**
+ */
+class KeyMap {
+ KeyToCommand *kmap;
+ int len;
+ int alloc;
+ static const KeyToCommand MapDefault[];
+
+public:
+ KeyMap();
+ ~KeyMap();
+ void Clear();
+ void AssignCmdKey(int key, int modifiers, unsigned int msg);
+ unsigned int Find(int key, int modifiers); // 0 returned on failure
+};
+
+#endif
diff --git a/src/KeyWords.cpp b/src/KeyWords.cpp
new file mode 100755
index 0000000..f82eb7d
--- /dev/null
+++ b/src/KeyWords.cpp
@@ -0,0 +1,221 @@
+// Scintilla source code edit control
+/** @file KeyWords.cxx
+ ** Colourise for particular languages.
+ **/
+// Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+const LexerModule *LexerModule::base = 0;
+int LexerModule::nextLanguage = SCLEX_AUTOMATIC+1;
+
+LexerModule::LexerModule(int language_,
+ LexerFunction fnLexer_,
+ const char *languageName_,
+ LexerFunction fnFolder_,
+ const char * const wordListDescriptions_[],
+ int styleBits_) :
+ language(language_),
+ fnLexer(fnLexer_),
+ fnFolder(fnFolder_),
+ wordListDescriptions(wordListDescriptions_),
+ styleBits(styleBits_),
+ languageName(languageName_) {
+ next = base;
+ base = this;
+ if (language == SCLEX_AUTOMATIC) {
+ language = nextLanguage;
+ nextLanguage++;
+ }
+}
+
+int LexerModule::GetNumWordLists() const {
+ if (wordListDescriptions == NULL) {
+ return -1;
+ } else {
+ int numWordLists = 0;
+
+ while (wordListDescriptions[numWordLists]) {
+ ++numWordLists;
+ }
+
+ return numWordLists;
+ }
+}
+
+const char *LexerModule::GetWordListDescription(int index) const {
+ static const char *emptyStr = "";
+
+ PLATFORM_ASSERT(index < GetNumWordLists());
+ if (index >= GetNumWordLists()) {
+ return emptyStr;
+ } else {
+ return wordListDescriptions[index];
+ }
+}
+
+int LexerModule::GetStyleBitsNeeded() const {
+ return styleBits;
+}
+
+const LexerModule *LexerModule::Find(int language) {
+ const LexerModule *lm = base;
+ while (lm) {
+ if (lm->language == language) {
+ return lm;
+ }
+ lm = lm->next;
+ }
+ return 0;
+}
+
+const LexerModule *LexerModule::Find(const char *languageName) {
+ if (languageName) {
+ const LexerModule *lm = base;
+ while (lm) {
+ if (lm->languageName && 0 == strcmp(lm->languageName, languageName)) {
+ return lm;
+ }
+ lm = lm->next;
+ }
+ }
+ return 0;
+}
+
+void LexerModule::Lex(unsigned int startPos, int lengthDoc, int initStyle,
+ WordList *keywordlists[], Accessor &styler) const {
+ if (fnLexer)
+ fnLexer(startPos, lengthDoc, initStyle, keywordlists, styler);
+}
+
+void LexerModule::Fold(unsigned int startPos, int lengthDoc, int initStyle,
+ WordList *keywordlists[], Accessor &styler) const {
+ if (fnFolder) {
+ int lineCurrent = styler.GetLine(startPos);
+ // Move back one line in case deletion wrecked current line fold state
+ if (lineCurrent > 0) {
+ lineCurrent--;
+ int newStartPos = styler.LineStart(lineCurrent);
+ lengthDoc += startPos - newStartPos;
+ startPos = newStartPos;
+ initStyle = 0;
+ if (startPos > 0) {
+ initStyle = styler.StyleAt(startPos - 1);
+ }
+ }
+ fnFolder(startPos, lengthDoc, initStyle, keywordlists, styler);
+ }
+}
+
+// Alternative historical name for Scintilla_LinkLexers
+int wxForceScintillaLexers(void) {
+ return Scintilla_LinkLexers();
+}
+
+// To add or remove a lexer, add or remove its file and run LexGen.py.
+
+// Force a reference to all of the Scintilla lexers so that the linker will
+// not remove the code of the lexers.
+int Scintilla_LinkLexers() {
+ static int forcer = 0;
+
+// Shorten the code that declares a lexer and ensures it is linked in by calling a method.
+#define LINK_LEXER(lexer) extern LexerModule lexer; forcer += lexer.GetLanguage();
+
+//++Autogenerated -- run src/LexGen.py to regenerate
+//**\(\tLINK_LEXER(\*);\n\)
+ LINK_LEXER(lmAda);
+ LINK_LEXER(lmAns1);
+ LINK_LEXER(lmAPDL);
+ LINK_LEXER(lmAsm);
+ LINK_LEXER(lmASP);
+ LINK_LEXER(lmAU3);
+ LINK_LEXER(lmAVE);
+ LINK_LEXER(lmBaan);
+ LINK_LEXER(lmBash);
+ LINK_LEXER(lmBatch);
+ LINK_LEXER(lmBlitzBasic);
+ LINK_LEXER(lmBullant);
+ LINK_LEXER(lmCaml);
+ LINK_LEXER(lmClw);
+ LINK_LEXER(lmClwNoCase);
+ LINK_LEXER(lmConf);
+ LINK_LEXER(lmCPP);
+ LINK_LEXER(lmCPPNoCase);
+ LINK_LEXER(lmCsound);
+ LINK_LEXER(lmCss);
+ LINK_LEXER(lmDiff);
+ LINK_LEXER(lmEiffel);
+ LINK_LEXER(lmEiffelkw);
+ LINK_LEXER(lmErlang);
+ LINK_LEXER(lmErrorList);
+ LINK_LEXER(lmESCRIPT);
+ LINK_LEXER(lmF77);
+ LINK_LEXER(lmFlagShip);
+ LINK_LEXER(lmForth);
+ LINK_LEXER(lmFortran);
+ LINK_LEXER(lmFreeBasic);
+ LINK_LEXER(lmGui4Cli);
+ LINK_LEXER(lmHaskell);
+ LINK_LEXER(lmHTML);
+ LINK_LEXER(lmInno);
+ LINK_LEXER(lmKix);
+ LINK_LEXER(lmLatex);
+ LINK_LEXER(lmLISP);
+ LINK_LEXER(lmLot);
+ LINK_LEXER(lmLout);
+ LINK_LEXER(lmLua);
+ LINK_LEXER(lmMake);
+ LINK_LEXER(lmMatlab);
+ LINK_LEXER(lmMETAPOST);
+ LINK_LEXER(lmMMIXAL);
+ LINK_LEXER(lmMSSQL);
+ LINK_LEXER(lmNncrontab);
+ LINK_LEXER(lmNsis);
+ LINK_LEXER(lmNull);
+ LINK_LEXER(lmOctave);
+ LINK_LEXER(lmOpal);
+ LINK_LEXER(lmPascal);
+ LINK_LEXER(lmPB);
+ LINK_LEXER(lmPerl);
+ LINK_LEXER(lmPHP);
+ LINK_LEXER(lmPHPSCRIPT);
+ LINK_LEXER(lmPOV);
+ LINK_LEXER(lmProps);
+ LINK_LEXER(lmPS);
+ LINK_LEXER(lmPureBasic);
+ LINK_LEXER(lmPython);
+ LINK_LEXER(lmREBOL);
+ LINK_LEXER(lmRuby);
+ LINK_LEXER(lmScriptol);
+ LINK_LEXER(lmSmalltalk);
+ LINK_LEXER(lmSpecman);
+ LINK_LEXER(lmSpice);
+ LINK_LEXER(lmSQL);
+ LINK_LEXER(lmTADS3);
+ LINK_LEXER(lmTCL);
+ LINK_LEXER(lmTeX);
+ LINK_LEXER(lmVB);
+ LINK_LEXER(lmVBScript);
+ LINK_LEXER(lmVerilog);
+ LINK_LEXER(lmVHDL);
+ LINK_LEXER(lmXML);
+ LINK_LEXER(lmYAML);
+
+//--Autogenerated -- end of automatically generated section
+
+ return 1;
+}
diff --git a/src/LexAPDL.cpp b/src/LexAPDL.cpp
new file mode 100755
index 0000000..1cf263e
--- /dev/null
+++ b/src/LexAPDL.cpp
@@ -0,0 +1,136 @@
+// Scintilla source code edit control
+/** @file LexAPDL.cxx
+ ** Lexer for APDL. Based on the lexer for Assembler by The Black Horus.
+ ** By Hadar Raz.
+ **/
+// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+
+static inline bool IsAWordChar(const int ch) {
+ return (ch < 0x80 && (isalnum(ch) || ch == '_'));
+}
+
+static inline bool IsAnOperator(char ch) {
+ // '.' left out as it is used to make up numbers
+ if (ch == '*' || ch == '/' || ch == '-' || ch == '+' ||
+ ch == '(' || ch == ')' || ch == '=' || ch == '^' ||
+ ch == '[' || ch == ']' || ch == '<' || ch == '&' ||
+ ch == '>' || ch == ',' || ch == '|' || ch == '~' ||
+ ch == '$' || ch == ':' || ch == '%')
+ return true;
+ return false;
+}
+
+static void ColouriseAPDLDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+ Accessor &styler) {
+
+ int stringStart = ' ';
+
+ WordList &processors = *keywordlists[0];
+ WordList &commands = *keywordlists[1];
+ WordList &slashcommands = *keywordlists[2];
+ WordList &starcommands = *keywordlists[3];
+ WordList &arguments = *keywordlists[4];
+ WordList &functions = *keywordlists[5];
+
+ // Do not leak onto next line
+ initStyle = SCE_APDL_DEFAULT;
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ for (; sc.More(); sc.Forward()) {
+ // Determine if the current state should terminate.
+ if (sc.state == SCE_APDL_NUMBER) {
+ if (!(IsADigit(sc.ch) || sc.ch == '.' || (sc.ch == 'e' || sc.ch == 'E') ||
+ ((sc.ch == '+' || sc.ch == '-') && (sc.chPrev == 'e' || sc.chPrev == 'E')))) {
+ sc.SetState(SCE_APDL_DEFAULT);
+ }
+ } else if (sc.state == SCE_APDL_COMMENT) {
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_APDL_DEFAULT);
+ }
+ } else if (sc.state == SCE_APDL_COMMENTBLOCK) {
+ if (sc.atLineEnd) {
+ if (sc.ch == '\r') {
+ sc.Forward();
+ }
+ sc.ForwardSetState(SCE_APDL_DEFAULT);
+ }
+ } else if (sc.state == SCE_APDL_STRING) {
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_APDL_DEFAULT);
+ } else if ((sc.ch == '\'' && stringStart == '\'') || (sc.ch == '\"' && stringStart == '\"')) {
+ sc.ForwardSetState(SCE_APDL_DEFAULT);
+ }
+ } else if (sc.state == SCE_APDL_WORD) {
+ if (!IsAWordChar(sc.ch)) {
+ char s[100];
+ sc.GetCurrentLowered(s, sizeof(s));
+ if (processors.InList(s)) {
+ sc.ChangeState(SCE_APDL_PROCESSOR);
+ } else if (slashcommands.InList(s)) {
+ sc.ChangeState(SCE_APDL_SLASHCOMMAND);
+ } else if (starcommands.InList(s)) {
+ sc.ChangeState(SCE_APDL_STARCOMMAND);
+ } else if (commands.InList(s)) {
+ sc.ChangeState(SCE_APDL_COMMAND);
+ } else if (arguments.InList(s)) {
+ sc.ChangeState(SCE_APDL_ARGUMENT);
+ } else if (functions.InList(s)) {
+ sc.ChangeState(SCE_APDL_FUNCTION);
+ }
+ sc.SetState(SCE_APDL_DEFAULT);
+ }
+ } else if (sc.state == SCE_APDL_OPERATOR) {
+ if (!IsAnOperator(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_APDL_DEFAULT);
+ }
+ }
+
+ // Determine if a new state should be entered.
+ if (sc.state == SCE_APDL_DEFAULT) {
+ if (sc.ch == '!' && sc.chNext == '!') {
+ sc.SetState(SCE_APDL_COMMENTBLOCK);
+ } else if (sc.ch == '!') {
+ sc.SetState(SCE_APDL_COMMENT);
+ } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
+ sc.SetState(SCE_APDL_NUMBER);
+ } else if (sc.ch == '\'' || sc.ch == '\"') {
+ sc.SetState(SCE_APDL_STRING);
+ stringStart = sc.ch;
+ } else if (IsAWordChar(sc.ch) || ((sc.ch == '*' || sc.ch == '/') && !isgraph(sc.chPrev))) {
+ sc.SetState(SCE_APDL_WORD);
+ } else if (IsAnOperator(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_APDL_OPERATOR);
+ }
+ }
+ }
+ sc.Complete();
+}
+
+static const char * const apdlWordListDesc[] = {
+ "processors",
+ "commands",
+ "slashommands",
+ "starcommands",
+ "arguments",
+ "functions",
+ 0
+};
+
+LexerModule lmAPDL(SCLEX_APDL, ColouriseAPDLDoc, "apdl", 0, apdlWordListDesc);
diff --git a/src/LexAU3.cpp b/src/LexAU3.cpp
new file mode 100755
index 0000000..2bc2e0f
--- /dev/null
+++ b/src/LexAU3.cpp
@@ -0,0 +1,891 @@
+// Scintilla source code edit control
+// @file LexAU3.cxx
+// Lexer for AutoIt3 http://www.hiddensoft.com/autoit3
+// by Jos van der Zande, jvdzande@yahoo.com
+//
+// Changes:
+// March 28, 2004 - Added the standard Folding code
+// April 21, 2004 - Added Preprosessor Table + Syntax Highlighting
+// Fixed Number highlighting
+// Changed default isoperator to IsAOperator to have a better match to AutoIt3
+// Fixed "#comments_start" -> "#comments-start"
+// Fixed "#comments_end" -> "#comments-end"
+// Fixed Sendkeys in Strings when not terminated with }
+// Added support for Sendkey strings that have second parameter e.g. {UP 5} or {a down}
+// April 26, 2004 - Fixed # pre-processor statement inside of comment block would invalidly change the color.
+// Added logic for #include <xyz.au3> to treat the <> as string
+// Added underscore to IsAOperator.
+// May 17, 2004 - Changed the folding logic from indent to keyword folding.
+// Added Folding logic for blocks of single-commentlines or commentblock.
+// triggered by: fold.comment=1
+// Added Folding logic for preprocessor blocks triggered by fold.preprocessor=1
+// Added Special for #region - #endregion syntax highlight and folding.
+// May 30, 2004 - Fixed issue with continuation lines on If statements.
+// June 5, 2004 - Added comma to Operators for better readability.
+// Added fold.compact support set with fold.compact=1
+// Changed folding inside of #cs-#ce. Default is no keyword folding inside comment blocks when fold.comment=1
+// it will now only happen when fold.comment=2.
+// Sep 5, 2004 - Added logic to handle colourizing words on the last line.
+// Typed Characters now show as "default" till they match any table.
+// Oct 10, 2004 - Added logic to show Comments in "Special" directives.
+// Nov 1, 2004 - Added better testing for Numbers supporting x and e notation.
+// Nov 28, 2004 - Added logic to handle continuation lines for syntax highlighting.
+// Jan 10, 2005 - Added Abbreviations Keyword used for expansion
+// Mar 24, 2005 - Updated Abbreviations Keywords to fix when followed by Operator.
+// Apr 18, 2005 - Updated #CE/#Comment-End logic to take a linecomment ";" into account
+// - Added folding support for With...EndWith
+// - Added support for a DOT in variable names
+// - Fixed Underscore in CommentBlock
+// May 23, 2005 - Fixed the SentKey lexing in case of a missing }
+// Aug 11, 2005 - Fixed possible bug with s_save length > 100.
+// Aug 23, 2005 - Added Switch/endswitch support to the folding logic.
+// Sep 27, 2005 - Fixed the SentKey lexing logic in case of multiple sentkeys.
+// Mar 12, 2006 - Fixed issue with <> coloring as String in stead of Operator in rare occasions.
+// Apr 8, 2006 - Added support for AutoIt3 Standard UDF library (SCE_AU3_UDF)
+//
+// Copyright for Scintilla: 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+// Scintilla source code edit control
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+static inline bool IsTypeCharacter(const int ch)
+{
+ return ch == '$';
+}
+static inline bool IsAWordChar(const int ch)
+{
+ return (ch < 0x80) && (isalnum(ch) || ch == '_');
+}
+
+static inline bool IsAWordStart(const int ch)
+{
+ return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '@' || ch == '#' || ch == '$' || ch == '.');
+}
+
+static inline bool IsAOperator(char ch) {
+ if (isascii(ch) && isalnum(ch))
+ return false;
+ if (ch == '+' || ch == '-' || ch == '*' || ch == '/' ||
+ ch == '&' || ch == '^' || ch == '=' || ch == '<' || ch == '>' ||
+ ch == '(' || ch == ')' || ch == '[' || ch == ']' || ch == ',' )
+ return true;
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// GetSendKey() filters the portion before and after a/multiple space(s)
+// and return the first portion to be looked-up in the table
+// also check if the second portion is valid... (up,down.on.off,toggle or a number)
+///////////////////////////////////////////////////////////////////////////////
+
+static int GetSendKey(const char *szLine, char *szKey)
+{
+ int nFlag = 0;
+ int nStartFound = 0;
+ int nKeyPos = 0;
+ int nSpecPos= 0;
+ int nSpecNum= 1;
+ int nPos = 0;
+ char cTemp;
+ char szSpecial[100];
+
+ // split the portion of the sendkey in the part before and after the spaces
+ while ( ( (cTemp = szLine[nPos]) != '\0'))
+ {
+ // skip leading Ctrl/Shift/Alt state
+ if (cTemp == '{') {
+ nStartFound = 1;
+ }
+ //
+ if (nStartFound == 1) {
+ if ((cTemp == ' ') && (nFlag == 0) ) // get the stuff till first space
+ {
+ nFlag = 1;
+ // Add } to the end of the first bit for table lookup later.
+ szKey[nKeyPos++] = '}';
+ }
+ else if (cTemp == ' ')
+ {
+ // skip other spaces
+ }
+ else if (nFlag == 0)
+ {
+ // save first portion into var till space or } is hit
+ szKey[nKeyPos++] = cTemp;
+ }
+ else if ((nFlag == 1) && (cTemp != '}'))
+ {
+ // Save second portion into var...
+ szSpecial[nSpecPos++] = cTemp;
+ // check if Second portion is all numbers for repeat fuction
+ if (isdigit(cTemp) == false) {nSpecNum = 0;}
+ }
+ }
+ nPos++; // skip to next char
+
+ } // End While
+
+
+ // Check if the second portion is either a number or one of these keywords
+ szKey[nKeyPos] = '\0';
+ szSpecial[nSpecPos] = '\0';
+ if (strcmp(szSpecial,"down")== 0 || strcmp(szSpecial,"up")== 0 ||
+ strcmp(szSpecial,"on")== 0 || strcmp(szSpecial,"off")== 0 ||
+ strcmp(szSpecial,"toggle")== 0 || nSpecNum == 1 )
+ {
+ nFlag = 0;
+ }
+ else
+ {
+ nFlag = 1;
+ }
+ return nFlag; // 1 is bad, 0 is good
+
+} // GetSendKey()
+
+//
+// Routine to check the last "none comment" character on a line to see if its a continuation
+//
+static bool IsContinuationLine(unsigned int szLine, Accessor &styler)
+{
+ int nsPos = styler.LineStart(szLine);
+ int nePos = styler.LineStart(szLine+1) - 2;
+ //int stylech = styler.StyleAt(nsPos);
+ while (nsPos < nePos)
+ {
+ //stylech = styler.StyleAt(nePos);
+ int stylech = styler.StyleAt(nsPos);
+ if (!(stylech == SCE_AU3_COMMENT)) {
+ char ch = styler.SafeGetCharAt(nePos);
+ if (!isspacechar(ch)) {
+ if (ch == '_')
+ return true;
+ else
+ return false;
+ }
+ }
+ nePos--; // skip to next char
+ } // End While
+ return false;
+} // IsContinuationLine()
+
+//
+// syntax highlighting logic
+static void ColouriseAU3Doc(unsigned int startPos,
+ int 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];
+ // find the first previous line without continuation character at the end
+ int lineCurrent = styler.GetLine(startPos);
+ int s_startPos = startPos;
+ // When not inside a Block comment: find First line without _
+ if (!(initStyle==SCE_AU3_COMMENTBLOCK)) {
+ while ((lineCurrent > 0 && IsContinuationLine(lineCurrent,styler)) ||
+ (lineCurrent > 1 && IsContinuationLine(lineCurrent-1,styler))) {
+ lineCurrent--;
+ startPos = styler.LineStart(lineCurrent); // get start position
+ initStyle = 0; // reset the start style to 0
+ }
+ }
+ // Set the new length to include it from the start and set the start position
+ length = length + s_startPos - startPos; // correct the total length to process
+ styler.StartAt(startPos);
+
+ StyleContext sc(startPos, length, initStyle, styler);
+ char si; // string indicator "=1 '=2
+ char ni; // Numeric indicator error=9 normal=0 normal+dec=1 hex=2 Enot=3
+ char ci; // comment indicator 0=not linecomment(;)
+ char s_save[100];
+ si=0;
+ ni=0;
+ ci=0;
+ //$$$
+ for (; sc.More(); sc.Forward()) {
+ char s[100];
+ sc.GetCurrentLowered(s, sizeof(s));
+ // **********************************************
+ // save the total current word for eof processing
+ if (IsAWordChar(sc.ch) || sc.ch == '}')
+ {
+ strcpy(s_save,s);
+ int tp = strlen(s_save);
+ if (tp < 99) {
+ s_save[tp] = static_cast<char>(tolower(sc.ch));
+ s_save[tp+1] = '\0';
+ }
+ }
+ // **********************************************
+ //
+ switch (sc.state)
+ {
+ case SCE_AU3_COMMENTBLOCK:
+ {
+ //Reset at line end
+ if (sc.atLineEnd) {
+ ci=0;
+ sc.SetState(SCE_AU3_COMMENTBLOCK);
+ }
+ //skip rest of line when a ; is encountered
+ if (sc.chPrev == ';') {
+ ci=2;
+ sc.SetState(SCE_AU3_COMMENTBLOCK);
+ }
+ // skip rest of the line
+ if (ci==2)
+ break;
+ // check when first character is detected on the line
+ if (ci==0) {
+ if (IsAWordStart(static_cast<char>(sc.ch)) || IsAOperator(static_cast<char>(sc.ch))) {
+ ci=1;
+ sc.SetState(SCE_AU3_COMMENTBLOCK);
+ }
+ break;
+ }
+ if (!(IsAWordChar(sc.ch) || (sc.ch == '-' && strcmp(s, "#comments") == 0))) {
+ if ((strcmp(s, "#ce")== 0 || strcmp(s, "#comments-end")== 0))
+ sc.SetState(SCE_AU3_COMMENT); // set to comment line for the rest of the line
+ else
+ ci=2; // line doesn't begin with #CE so skip the rest of the line
+ }
+ break;
+ }
+ case SCE_AU3_COMMENT:
+ {
+ if (sc.atLineEnd) {sc.SetState(SCE_AU3_DEFAULT);}
+ break;
+ }
+ case SCE_AU3_OPERATOR:
+ {
+ // check if its a COMobject
+ if (sc.chPrev == '.' && IsAWordChar(sc.ch)) {
+ sc.SetState(SCE_AU3_COMOBJ);
+ }
+ else {
+ sc.SetState(SCE_AU3_DEFAULT);
+ }
+ break;
+ }
+ case SCE_AU3_SPECIAL:
+ {
+ if (sc.ch == ';') {sc.SetState(SCE_AU3_COMMENT);}
+ if (sc.atLineEnd) {sc.SetState(SCE_AU3_DEFAULT);}
+ break;
+ }
+ case SCE_AU3_KEYWORD:
+ {
+ if (!(IsAWordChar(sc.ch) || (sc.ch == '-' && (strcmp(s, "#comments") == 0 || strcmp(s, "#include") == 0))))
+ {
+ if (!IsTypeCharacter(sc.ch))
+ {
+ if (strcmp(s, "#cs")== 0 || strcmp(s, "#comments-start")== 0 )
+ {
+ sc.ChangeState(SCE_AU3_COMMENTBLOCK);
+ sc.SetState(SCE_AU3_COMMENTBLOCK);
+ }
+ else if (keywords.InList(s)) {
+ sc.ChangeState(SCE_AU3_KEYWORD);
+ sc.SetState(SCE_AU3_DEFAULT);
+ }
+ else if (keywords2.InList(s)) {
+ sc.ChangeState(SCE_AU3_FUNCTION);
+ sc.SetState(SCE_AU3_DEFAULT);
+ }
+ else if (keywords3.InList(s)) {
+ sc.ChangeState(SCE_AU3_MACRO);
+ sc.SetState(SCE_AU3_DEFAULT);
+ }
+ else if (keywords5.InList(s)) {
+ sc.ChangeState(SCE_AU3_PREPROCESSOR);
+ sc.SetState(SCE_AU3_DEFAULT);
+ if (strcmp(s, "#include")== 0)
+ {
+ si = 3; // use to determine string start for #inlude <>
+ }
+ }
+ else if (keywords6.InList(s)) {
+ sc.ChangeState(SCE_AU3_SPECIAL);
+ sc.SetState(SCE_AU3_SPECIAL);
+ }
+ else if ((keywords7.InList(s)) && (!IsAOperator(static_cast<char>(sc.ch)))) {
+ sc.ChangeState(SCE_AU3_EXPAND);
+ sc.SetState(SCE_AU3_DEFAULT);
+ }
+ else if (keywords8.InList(s)) {
+ sc.ChangeState(SCE_AU3_UDF);
+ sc.SetState(SCE_AU3_DEFAULT);
+ }
+ else if (strcmp(s, "_") == 0) {
+ sc.ChangeState(SCE_AU3_OPERATOR);
+ sc.SetState(SCE_AU3_DEFAULT);
+ }
+ else if (!IsAWordChar(sc.ch)) {
+ sc.ChangeState(SCE_AU3_DEFAULT);
+ sc.SetState(SCE_AU3_DEFAULT);
+ }
+ }
+ }
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_AU3_DEFAULT);}
+ break;
+ }
+ case SCE_AU3_NUMBER:
+ {
+ // Numeric indicator error=9 normal=0 normal+dec=1 hex=2 E-not=3
+ //
+ // test for Hex notation
+ if (strcmp(s, "0") == 0 && (sc.ch == 'x' || sc.ch == 'X') && ni == 0)
+ {
+ ni = 2;
+ break;
+ }
+ // test for E notation
+ if (IsADigit(sc.chPrev) && (sc.ch == 'e' || sc.ch == 'E') && ni <= 1)
+ {
+ ni = 3;
+ break;
+ }
+ // Allow Hex characters inside hex numeric strings
+ if ((ni == 2) &&
+ (sc.ch == 'a' || sc.ch == 'b' || sc.ch == 'c' || sc.ch == 'd' || sc.ch == 'e' || sc.ch == 'f' ||
+ sc.ch == 'A' || sc.ch == 'B' || sc.ch == 'C' || sc.ch == 'D' || sc.ch == 'E' || sc.ch == 'F' ))
+ {
+ break;
+ }
+ // test for 1 dec point only
+ if (sc.ch == '.')
+ {
+ if (ni==0)
+ {
+ ni=1;
+ }
+ else
+ {
+ ni=9;
+ }
+ break;
+ }
+ // end of numeric string ?
+ if (!(IsADigit(sc.ch)))
+ {
+ if (ni==9)
+ {
+ sc.ChangeState(SCE_AU3_DEFAULT);
+ }
+ sc.SetState(SCE_AU3_DEFAULT);
+ }
+ break;
+ }
+ case SCE_AU3_VARIABLE:
+ {
+ // Check if its a COMObject
+ if (sc.ch == '.' && !IsADigit(sc.chNext)) {
+ sc.SetState(SCE_AU3_OPERATOR);
+ }
+ else if (!IsAWordChar(sc.ch)) {
+ sc.SetState(SCE_AU3_DEFAULT);
+ }
+ break;
+ }
+ case SCE_AU3_COMOBJ:
+ {
+ if (!(IsAWordChar(sc.ch))) {
+ sc.SetState(SCE_AU3_DEFAULT);
+ }
+ break;
+ }
+ case SCE_AU3_STRING:
+ {
+ // check for " to end a double qouted string or
+ // check for ' to end a single qouted string
+ if ((si == 1 && sc.ch == '\"') || (si == 2 && sc.ch == '\'') || (si == 3 && sc.ch == '>'))
+ {
+ sc.ForwardSetState(SCE_AU3_DEFAULT);
+ si=0;
+ }
+ if (sc.atLineEnd)
+ {
+ si=0;
+ // at line end and not found a continuation char then reset to default
+ int lineCurrent = styler.GetLine(sc.currentPos);
+ if (!IsContinuationLine(lineCurrent,styler))
+ {
+ sc.SetState(SCE_AU3_DEFAULT);
+ }
+ }
+ // find Sendkeys in a STRING
+ if (sc.ch == '{' || sc.ch == '+' || sc.ch == '!' || sc.ch == '^' || sc.ch == '#' ) {
+ sc.SetState(SCE_AU3_SENT);}
+ break;
+ }
+
+ case SCE_AU3_SENT:
+ {
+ // Send key string ended
+ if (sc.chPrev == '}' && sc.ch != '}')
+ {
+ // set color to SENDKEY when valid sendkey .. else set back to regular string
+ char sk[100];
+ // split {111 222} and return {111} and check if 222 is valid.
+ // if return code = 1 then invalid 222 so must be string
+ if (GetSendKey(s,sk))
+ {
+ sc.ChangeState(SCE_AU3_STRING);
+ }
+ // if single char between {?} then its ok as sendkey for a single character
+ else if (strlen(sk) == 3)
+ {
+ sc.ChangeState(SCE_AU3_SENT);
+ }
+ // if sendkey {111} is in table then ok as sendkey
+ else if (keywords4.InList(sk))
+ {
+ sc.ChangeState(SCE_AU3_SENT);
+ }
+ else
+ {
+ sc.ChangeState(SCE_AU3_STRING);
+ }
+ sc.SetState(SCE_AU3_STRING);
+ }
+ else
+ {
+ // check if the start is a valid SendKey start
+ int nPos = 0;
+ int nState = 1;
+ char cTemp;
+ while (!(nState == 2) && ((cTemp = s[nPos]) != '\0'))
+ {
+ if (cTemp == '{' && nState == 1)
+ {
+ nState = 2;
+ }
+ if (nState == 1 && !(cTemp == '+' || cTemp == '!' || cTemp == '^' || cTemp == '#' ))
+ {
+ nState = 0;
+ }
+ nPos++;
+ }
+ //Verify characters infront of { ... if not assume regular string
+ if (nState == 1 && (!(sc.ch == '{' || sc.ch == '+' || sc.ch == '!' || sc.ch == '^' || sc.ch == '#' ))) {
+ sc.ChangeState(SCE_AU3_STRING);
+ sc.SetState(SCE_AU3_STRING);
+ }
+ // If invalid character found then assume its a regular string
+ if (nState == 0) {
+ sc.ChangeState(SCE_AU3_STRING);
+ sc.SetState(SCE_AU3_STRING);
+ }
+ }
+ // check if next portion is again a sendkey
+ if (sc.atLineEnd)
+ {
+ sc.ChangeState(SCE_AU3_STRING);
+ sc.SetState(SCE_AU3_DEFAULT);
+ si = 0; // reset string indicator
+ }
+ //* check in next characters following a sentkey are again a sent key
+ // Need this test incase of 2 sentkeys like {F1}{ENTER} but not detect {{}
+ if (sc.state == SCE_AU3_STRING && (sc.ch == '{' || sc.ch == '+' || sc.ch == '!' || sc.ch == '^' || sc.ch == '#' )) {
+ sc.SetState(SCE_AU3_SENT);}
+ // check to see if the string ended...
+ // Sendkey string isn't complete but the string ended....
+ if ((si == 1 && sc.ch == '\"') || (si == 2 && sc.ch == '\''))
+ {
+ sc.ChangeState(SCE_AU3_STRING);
+ sc.ForwardSetState(SCE_AU3_DEFAULT);
+ }
+ break;
+ }
+ } //switch (sc.state)
+
+ // Determine if a new state should be entered:
+
+ if (sc.state == SCE_AU3_DEFAULT)
+ {
+ if (sc.ch == ';') {sc.SetState(SCE_AU3_COMMENT);}
+ else if (sc.ch == '#') {sc.SetState(SCE_AU3_KEYWORD);}
+ else if (sc.ch == '$') {sc.SetState(SCE_AU3_VARIABLE);}
+ else if (sc.ch == '.' && !IsADigit(sc.chNext)) {sc.SetState(SCE_AU3_OPERATOR);}
+ else if (sc.ch == '@') {sc.SetState(SCE_AU3_KEYWORD);}
+ //else if (sc.ch == '_') {sc.SetState(SCE_AU3_KEYWORD);}
+ else if (sc.ch == '<' && si==3) {sc.SetState(SCE_AU3_STRING);} // string after #include
+ else if (sc.ch == '\"') {
+ sc.SetState(SCE_AU3_STRING);
+ si = 1; }
+ else if (sc.ch == '\'') {
+ sc.SetState(SCE_AU3_STRING);
+ si = 2; }
+ else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext)))
+ {
+ sc.SetState(SCE_AU3_NUMBER);
+ ni = 0;
+ }
+ else if (IsAWordStart(sc.ch)) {sc.SetState(SCE_AU3_KEYWORD);}
+ else if (IsAOperator(static_cast<char>(sc.ch))) {sc.SetState(SCE_AU3_OPERATOR);}
+ else if (sc.atLineEnd) {sc.SetState(SCE_AU3_DEFAULT);}
+ }
+ } //for (; sc.More(); sc.Forward())
+
+ //*************************************
+ // Colourize the last word correctly
+ //*************************************
+ if (sc.state == SCE_AU3_KEYWORD)
+ {
+ if (strcmp(s_save, "#cs")== 0 || strcmp(s_save, "#comments-start")== 0 )
+ {
+ sc.ChangeState(SCE_AU3_COMMENTBLOCK);
+ sc.SetState(SCE_AU3_COMMENTBLOCK);
+ }
+ else if (keywords.InList(s_save)) {
+ sc.ChangeState(SCE_AU3_KEYWORD);
+ sc.SetState(SCE_AU3_KEYWORD);
+ }
+ else if (keywords2.InList(s_save)) {
+ sc.ChangeState(SCE_AU3_FUNCTION);
+ sc.SetState(SCE_AU3_FUNCTION);
+ }
+ else if (keywords3.InList(s_save)) {
+ sc.ChangeState(SCE_AU3_MACRO);
+ sc.SetState(SCE_AU3_MACRO);
+ }
+ else if (keywords5.InList(s_save)) {
+ sc.ChangeState(SCE_AU3_PREPROCESSOR);
+ sc.SetState(SCE_AU3_PREPROCESSOR);
+ }
+ else if (keywords6.InList(s_save)) {
+ sc.ChangeState(SCE_AU3_SPECIAL);
+ sc.SetState(SCE_AU3_SPECIAL);
+ }
+ else if (keywords7.InList(s_save) && sc.atLineEnd) {
+ sc.ChangeState(SCE_AU3_EXPAND);
+ sc.SetState(SCE_AU3_EXPAND);
+ }
+ else if (keywords8.InList(s_save)) {
+ sc.ChangeState(SCE_AU3_UDF);
+ sc.SetState(SCE_AU3_UDF);
+ }
+ else {
+ sc.ChangeState(SCE_AU3_DEFAULT);
+ sc.SetState(SCE_AU3_DEFAULT);
+ }
+ }
+ if (sc.state == SCE_AU3_SENT)
+ {
+ // Send key string ended
+ if (sc.chPrev == '}' && sc.ch != '}')
+ {
+ // set color to SENDKEY when valid sendkey .. else set back to regular string
+ char sk[100];
+ // split {111 222} and return {111} and check if 222 is valid.
+ // if return code = 1 then invalid 222 so must be string
+ if (GetSendKey(s_save,sk))
+ {
+ sc.ChangeState(SCE_AU3_STRING);
+ }
+ // if single char between {?} then its ok as sendkey for a single character
+ else if (strlen(sk) == 3)
+ {
+ sc.ChangeState(SCE_AU3_SENT);
+ }
+ // if sendkey {111} is in table then ok as sendkey
+ else if (keywords4.InList(sk))
+ {
+ sc.ChangeState(SCE_AU3_SENT);
+ }
+ else
+ {
+ sc.ChangeState(SCE_AU3_STRING);
+ }
+ sc.SetState(SCE_AU3_STRING);
+ }
+ // check if next portion is again a sendkey
+ if (sc.atLineEnd)
+ {
+ sc.ChangeState(SCE_AU3_STRING);
+ sc.SetState(SCE_AU3_DEFAULT);
+ }
+ }
+ //*************************************
+ sc.Complete();
+}
+
+//
+static bool IsStreamCommentStyle(int style) {
+ return style == SCE_AU3_COMMENT || style == SCE_AU3_COMMENTBLOCK;
+}
+
+//
+// Routine to find first none space on the current line and return its Style
+// needed for comment lines not starting on pos 1
+static int GetStyleFirstWord(unsigned int szLine, Accessor &styler)
+{
+ int nsPos = styler.LineStart(szLine);
+ int nePos = styler.LineStart(szLine+1) - 1;
+ while (isspacechar(styler.SafeGetCharAt(nsPos)) && nsPos < nePos)
+ {
+ nsPos++; // skip to next char
+
+ } // End While
+ return styler.StyleAt(nsPos);
+
+} // GetStyleFirstWord()
+
+
+//
+static void FoldAU3Doc(unsigned int startPos, int length, int, WordList *[], Accessor &styler)
+{
+ int endPos = startPos + length;
+ // get settings from the config files for folding comments and preprocessor lines
+ bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
+ bool foldInComment = styler.GetPropertyInt("fold.comment") == 2;
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ bool foldpreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0;
+ // Backtrack to previous line in case need to fix its fold status
+ int lineCurrent = styler.GetLine(startPos);
+ if (startPos > 0) {
+ if (lineCurrent > 0) {
+ lineCurrent--;
+ startPos = styler.LineStart(lineCurrent);
+ }
+ }
+ // vars for style of previous/current/next lines
+ int style = GetStyleFirstWord(lineCurrent,styler);
+ int stylePrev = 0;
+ // find the first previous line without continuation character at the end
+ while ((lineCurrent > 0 && IsContinuationLine(lineCurrent,styler)) ||
+ (lineCurrent > 1 && IsContinuationLine(lineCurrent-1,styler))) {
+ lineCurrent--;
+ startPos = styler.LineStart(lineCurrent);
+ }
+ if (lineCurrent > 0) {
+ stylePrev = GetStyleFirstWord(lineCurrent-1,styler);
+ }
+ // vars for getting first word to check for keywords
+ bool FirstWordStart = false;
+ bool FirstWordEnd = false;
+ char szKeyword[10]="";
+ int szKeywordlen = 0;
+ char szThen[5]="";
+ int szThenlen = 0;
+ bool ThenFoundLast = false;
+ // var for indentlevel
+ int levelCurrent = SC_FOLDLEVELBASE;
+ if (lineCurrent > 0)
+ levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
+ int levelNext = levelCurrent;
+ //
+ int visibleChars = 0;
+ char chNext = styler.SafeGetCharAt(startPos);
+ char chPrev = ' ';
+ //
+ for (int i = startPos; i < endPos; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ if (IsAWordChar(ch)) {
+ visibleChars++;
+ }
+ // get the syle for the current character neede to check in comment
+ int stylech = styler.StyleAt(i);
+ // get first word for the line for indent check max 9 characters
+ if (FirstWordStart && (!(FirstWordEnd))) {
+ if (!IsAWordChar(ch)) {
+ FirstWordEnd = true;
+ szKeyword[szKeywordlen] = '\0';
+ }
+ else {
+ if (szKeywordlen < 10) {
+ szKeyword[szKeywordlen++] = static_cast<char>(tolower(ch));
+ }
+ }
+ }
+ // start the capture of the first word
+ if (!(FirstWordStart)) {
+ if (IsAWordChar(ch) || IsAWordStart(ch) || ch == ';') {
+ FirstWordStart = true;
+ szKeyword[szKeywordlen++] = static_cast<char>(tolower(ch));
+ }
+ }
+ // only process this logic when not in comment section
+ if (!(stylech == SCE_AU3_COMMENT)) {
+ if (ThenFoundLast) {
+ if (IsAWordChar(ch)) {
+ ThenFoundLast = false;
+ }
+ }
+ // find out if the word "then" is the last on a "if" line
+ if (FirstWordEnd && strcmp(szKeyword,"if") == 0) {
+ if (szThenlen == 4) {
+ szThen[0] = szThen[1];
+ szThen[1] = szThen[2];
+ szThen[2] = szThen[3];
+ szThen[3] = static_cast<char>(tolower(ch));
+ if (strcmp(szThen,"then") == 0 ) {
+ ThenFoundLast = true;
+ }
+ }
+ else {
+ szThen[szThenlen++] = static_cast<char>(tolower(ch));
+ if (szThenlen == 5) {
+ szThen[4] = '\0';
+ }
+ }
+ }
+ }
+ // End of Line found so process the information
+ if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == endPos)) {
+ // **************************
+ // Folding logic for Keywords
+ // **************************
+ // if a keyword is found on the current line and the line doesn't end with _ (continuation)
+ // and we are not inside a commentblock.
+ if (szKeywordlen > 0 && (!(chPrev == '_')) &&
+ ((!(IsStreamCommentStyle(style)) || foldInComment)) ) {
+ szKeyword[szKeywordlen] = '\0';
+ // only fold "if" last keyword is "then" (else its a one line if)
+ if (strcmp(szKeyword,"if") == 0 && ThenFoundLast) {
+ levelNext++;
+ }
+ // create new fold for these words
+ if (strcmp(szKeyword,"do") == 0 || strcmp(szKeyword,"for") == 0 ||
+ strcmp(szKeyword,"func") == 0 || strcmp(szKeyword,"while") == 0||
+ strcmp(szKeyword,"with") == 0 || strcmp(szKeyword,"#region") == 0 ) {
+ levelNext++;
+ }
+ // create double Fold for select&switch because Case will subtract one of the current level
+ if (strcmp(szKeyword,"select") == 0 || strcmp(szKeyword,"switch") == 0) {
+ levelNext++;
+ levelNext++;
+ }
+ // end the fold for these words before the current line
+ if (strcmp(szKeyword,"endfunc") == 0 || strcmp(szKeyword,"endif") == 0 ||
+ strcmp(szKeyword,"next") == 0 || strcmp(szKeyword,"until") == 0 ||
+ strcmp(szKeyword,"endwith") == 0 ||strcmp(szKeyword,"wend") == 0){
+ levelNext--;
+ levelCurrent--;
+ }
+ // end the fold for these words before the current line and Start new fold
+ if (strcmp(szKeyword,"case") == 0 || strcmp(szKeyword,"else") == 0 ||
+ strcmp(szKeyword,"elseif") == 0 ) {
+ levelCurrent--;
+ }
+ // end the double fold for this word before the current line
+ if (strcmp(szKeyword,"endselect") == 0 || strcmp(szKeyword,"endswitch") == 0 ) {
+ levelNext--;
+ levelNext--;
+ levelCurrent--;
+ levelCurrent--;
+ }
+ // end the fold for these words on the current line
+ if (strcmp(szKeyword,"#endregion") == 0 ) {
+ levelNext--;
+ }
+ }
+ // Preprocessor and Comment folding
+ int styleNext = GetStyleFirstWord(lineCurrent + 1,styler);
+ // *************************************
+ // Folding logic for preprocessor blocks
+ // *************************************
+ // process preprosessor line
+ if (foldpreprocessor && style == SCE_AU3_PREPROCESSOR) {
+ if (!(stylePrev == SCE_AU3_PREPROCESSOR) && (styleNext == SCE_AU3_PREPROCESSOR)) {
+ levelNext++;
+ }
+ // fold till the last line for normal comment lines
+ else if (stylePrev == SCE_AU3_PREPROCESSOR && !(styleNext == SCE_AU3_PREPROCESSOR)) {
+ levelNext--;
+ }
+ }
+ // *********************************
+ // Folding logic for Comment blocks
+ // *********************************
+ if (foldComment && IsStreamCommentStyle(style)) {
+ // Start of a comment block
+ if (!(stylePrev==style) && IsStreamCommentStyle(styleNext) && styleNext==style) {
+ levelNext++;
+ }
+ // fold till the last line for normal comment lines
+ else if (IsStreamCommentStyle(stylePrev)
+ && !(styleNext == SCE_AU3_COMMENT)
+ && stylePrev == SCE_AU3_COMMENT
+ && style == SCE_AU3_COMMENT) {
+ levelNext--;
+ }
+ // fold till the one but last line for Blockcomment lines
+ else if (IsStreamCommentStyle(stylePrev)
+ && !(styleNext == SCE_AU3_COMMENTBLOCK)
+ && style == SCE_AU3_COMMENTBLOCK) {
+ levelNext--;
+ levelCurrent--;
+ }
+ }
+ int levelUse = levelCurrent;
+ int lev = levelUse | levelNext << 16;
+ if (visibleChars == 0 && foldCompact)
+ lev |= SC_FOLDLEVELWHITEFLAG;
+ if (levelUse < levelNext) {
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ }
+ if (lev != styler.LevelAt(lineCurrent)) {
+ styler.SetLevel(lineCurrent, lev);
+ }
+ // reset values for the next line
+ lineCurrent++;
+ stylePrev = style;
+ style = styleNext;
+ levelCurrent = levelNext;
+ visibleChars = 0;
+ // if the last character is an Underscore then don't reset since the line continues on the next line.
+ if (!(chPrev == '_')) {
+ szKeywordlen = 0;
+ szThenlen = 0;
+ FirstWordStart = false;
+ FirstWordEnd = false;
+ ThenFoundLast = false;
+ }
+ }
+ // save the last processed character
+ if (!isspacechar(ch)) {
+ chPrev = ch;
+ visibleChars++;
+ }
+ }
+}
+
+
+//
+
+static const char * const AU3WordLists[] = {
+ "#autoit keywords",
+ "#autoit functions",
+ "#autoit macros",
+ "#autoit Sent keys",
+ "#autoit Pre-processors",
+ "#autoit Special",
+ "#autoit Expand",
+ "#autoit UDF",
+ 0
+};
+LexerModule lmAU3(SCLEX_AU3, ColouriseAU3Doc, "au3", FoldAU3Doc , AU3WordLists);
diff --git a/src/LexAVE.cpp b/src/LexAVE.cpp
new file mode 100755
index 0000000..e30ee7d
--- /dev/null
+++ b/src/LexAVE.cpp
@@ -0,0 +1,225 @@
+// SciTE - Scintilla based Text Editor
+/** @file LexAVE.cxx
+ ** Lexer for Avenue.
+ **
+ ** Written by Alexey Yutkin <yutkin@geol.msu.ru>.
+ **/
+// Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+
+
+static inline bool IsAWordChar(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_');
+}
+static inline bool IsEnumChar(const int ch) {
+ return (ch < 0x80) && (isalnum(ch)|| ch == '_');
+}
+static inline bool IsANumberChar(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '.' );
+}
+
+inline bool IsAWordStart(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '_');
+}
+
+inline bool isAveOperator(char ch) {
+ if (isalnum(ch))
+ return false;
+ // '.' left out as it is used to make up numbers
+ if (ch == '*' || ch == '/' || ch == '-' || ch == '+' ||
+ ch == '(' || ch == ')' || ch == '=' ||
+ ch == '{' || ch == '}' ||
+ ch == '[' || ch == ']' || ch == ';' ||
+ ch == '<' || ch == '>' || ch == ',' ||
+ ch == '.' )
+ return true;
+ return false;
+}
+
+static void ColouriseAveDoc(
+ unsigned int startPos,
+ int 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];
+
+ // Do not leak onto next line
+ if (initStyle == SCE_AVE_STRINGEOL) {
+ initStyle = SCE_AVE_DEFAULT;
+ }
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ for (; sc.More(); sc.Forward()) {
+ if (sc.atLineEnd) {
+ // Update the line state, so it can be seen by next line
+ int currentLine = styler.GetLine(sc.currentPos);
+ styler.SetLineState(currentLine, 0);
+ }
+ if (sc.atLineStart && (sc.state == SCE_AVE_STRING)) {
+ // Prevent SCE_AVE_STRINGEOL from leaking back to previous line
+ sc.SetState(SCE_AVE_STRING);
+ }
+
+
+ // Determine if the current state should terminate.
+ if (sc.state == SCE_AVE_OPERATOR) {
+ sc.SetState(SCE_AVE_DEFAULT);
+ } else if (sc.state == SCE_AVE_NUMBER) {
+ if (!IsANumberChar(sc.ch)) {
+ sc.SetState(SCE_AVE_DEFAULT);
+ }
+ } else if (sc.state == SCE_AVE_ENUM) {
+ if (!IsEnumChar(sc.ch)) {
+ sc.SetState(SCE_AVE_DEFAULT);
+ }
+ } else if (sc.state == SCE_AVE_IDENTIFIER) {
+ if (!IsAWordChar(sc.ch) || (sc.ch == '.')) {
+ char s[100];
+ //sc.GetCurrent(s, sizeof(s));
+ sc.GetCurrentLowered(s, sizeof(s));
+ if (keywords.InList(s)) {
+ sc.ChangeState(SCE_AVE_WORD);
+ } else if (keywords2.InList(s)) {
+ sc.ChangeState(SCE_AVE_WORD2);
+ } else if (keywords3.InList(s)) {
+ sc.ChangeState(SCE_AVE_WORD3);
+ } else if (keywords4.InList(s)) {
+ sc.ChangeState(SCE_AVE_WORD4);
+ } else if (keywords5.InList(s)) {
+ sc.ChangeState(SCE_AVE_WORD5);
+ } else if (keywords6.InList(s)) {
+ sc.ChangeState(SCE_AVE_WORD6);
+ }
+ sc.SetState(SCE_AVE_DEFAULT);
+ }
+ } else if (sc.state == SCE_AVE_COMMENT) {
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_AVE_DEFAULT);
+ }
+ } else if (sc.state == SCE_AVE_STRING) {
+ if (sc.ch == '\"') {
+ sc.ForwardSetState(SCE_AVE_DEFAULT);
+ } else if (sc.atLineEnd) {
+ sc.ChangeState(SCE_AVE_STRINGEOL);
+ sc.ForwardSetState(SCE_AVE_DEFAULT);
+ }
+ }
+
+ // Determine if a new state should be entered.
+ if (sc.state == SCE_AVE_DEFAULT) {
+ if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
+ sc.SetState(SCE_AVE_NUMBER);
+ } else if (IsAWordStart(sc.ch)) {
+ sc.SetState(SCE_AVE_IDENTIFIER);
+ } else if (sc.Match('\"')) {
+ sc.SetState(SCE_AVE_STRING);
+ } else if (sc.Match('\'')) {
+ sc.SetState(SCE_AVE_COMMENT);
+ sc.Forward();
+ } else if (isAveOperator(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_AVE_OPERATOR);
+ } else if (sc.Match('#')) {
+ sc.SetState(SCE_AVE_ENUM);
+ sc.Forward();
+ }
+ }
+ }
+ sc.Complete();
+}
+
+static void FoldAveDoc(unsigned int startPos, int length, int /* initStyle */, WordList *[],
+ Accessor &styler) {
+ unsigned int lengthDoc = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
+ int levelCurrent = levelPrev;
+ char chNext = static_cast<char>(tolower(styler[startPos]));
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ int styleNext = styler.StyleAt(startPos);
+ char s[10];
+
+ for (unsigned int i = startPos; i < lengthDoc; i++) {
+ char ch = static_cast<char>(tolower(chNext));
+ chNext = static_cast<char>(tolower(styler.SafeGetCharAt(i + 1)));
+ int style = styleNext;
+ styleNext = styler.StyleAt(i + 1);
+ bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
+ if (style == SCE_AVE_WORD) {
+ if (ch == 't' || ch == 'f' || ch == 'w' || ch == 'e') {
+ for (unsigned int j = 0; j < 6; j++) {
+ if (!iswordchar(styler[i + j])) {
+ break;
+ }
+ s[j] = static_cast<char>(tolower(styler[i + j]));
+ s[j + 1] = '\0';
+ }
+
+ if ((strcmp(s, "then") == 0) || (strcmp(s, "for") == 0) || (strcmp(s, "while") == 0)) {
+ levelCurrent++;
+ }
+ if ((strcmp(s, "end") == 0) || (strcmp(s, "elseif") == 0)) {
+ // Normally "elseif" and "then" will be on the same line and will cancel
+ // each other out. // As implemented, this does not support fold.at.else.
+ levelCurrent--;
+ }
+ }
+ } else if (style == SCE_AVE_OPERATOR) {
+ if (ch == '{' || ch == '(') {
+ levelCurrent++;
+ } else if (ch == '}' || ch == ')') {
+ levelCurrent--;
+ }
+ }
+
+ if (atEOL) {
+ int lev = levelPrev;
+ if (visibleChars == 0 && foldCompact) {
+ 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);
+}
+
+LexerModule lmAVE(SCLEX_AVE, ColouriseAveDoc, "ave", FoldAveDoc);
+
diff --git a/src/LexAda.cpp b/src/LexAda.cpp
new file mode 100755
index 0000000..0227ce1
--- /dev/null
+++ b/src/LexAda.cpp
@@ -0,0 +1,520 @@
+// Scintilla source code edit control
+/** @file LexAda.cxx
+ ** Lexer for Ada 95
+ **/
+// Copyright 2002 by Sergey Koshcheyev <sergey.k@seznam.cz>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "Platform.h"
+
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "PropSet.h"
+#include "KeyWords.h"
+#include "SciLexer.h"
+#include "SString.h"
+
+/*
+ * Interface
+ */
+
+static void ColouriseDocument(
+ unsigned int startPos,
+ int length,
+ int initStyle,
+ WordList *keywordlists[],
+ Accessor &styler);
+
+static const char * const adaWordListDesc[] = {
+ "Keywords",
+ 0
+};
+
+LexerModule lmAda(SCLEX_ADA, ColouriseDocument, "ada", NULL, adaWordListDesc);
+
+/*
+ * Implementation
+ */
+
+// Functions that have apostropheStartsAttribute as a parameter set it according to whether
+// an apostrophe encountered after processing the current token will start an attribute or
+// a character literal.
+static void ColouriseCharacter(StyleContext& sc, bool& apostropheStartsAttribute);
+static void ColouriseComment(StyleContext& sc, bool& apostropheStartsAttribute);
+static void ColouriseContext(StyleContext& sc, char chEnd, int stateEOL);
+static void ColouriseDelimiter(StyleContext& sc, bool& apostropheStartsAttribute);
+static void ColouriseLabel(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute);
+static void ColouriseNumber(StyleContext& sc, bool& apostropheStartsAttribute);
+static void ColouriseString(StyleContext& sc, bool& apostropheStartsAttribute);
+static void ColouriseWhiteSpace(StyleContext& sc, bool& apostropheStartsAttribute);
+static void ColouriseWord(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute);
+
+static inline bool IsDelimiterCharacter(int ch);
+static inline bool IsNumberStartCharacter(int ch);
+static inline bool IsNumberCharacter(int ch);
+static inline bool IsSeparatorOrDelimiterCharacter(int ch);
+static bool IsValidIdentifier(const SString& identifier);
+static bool IsValidNumber(const SString& number);
+static inline bool IsWordStartCharacter(int ch);
+static inline bool IsWordCharacter(int ch);
+
+static void ColouriseCharacter(StyleContext& sc, bool& apostropheStartsAttribute) {
+ apostropheStartsAttribute = true;
+
+ sc.SetState(SCE_ADA_CHARACTER);
+
+ // Skip the apostrophe and one more character (so that '' is shown as non-terminated and '''
+ // is handled correctly)
+ sc.Forward();
+ sc.Forward();
+
+ ColouriseContext(sc, '\'', SCE_ADA_CHARACTEREOL);
+}
+
+static void ColouriseContext(StyleContext& sc, char chEnd, int stateEOL) {
+ while (!sc.atLineEnd && !sc.Match(chEnd)) {
+ sc.Forward();
+ }
+
+ if (!sc.atLineEnd) {
+ sc.ForwardSetState(SCE_ADA_DEFAULT);
+ } else {
+ sc.ChangeState(stateEOL);
+ }
+}
+
+static void ColouriseComment(StyleContext& sc, bool& /*apostropheStartsAttribute*/) {
+ // Apostrophe meaning is not changed, but the parameter is present for uniformity
+
+ sc.SetState(SCE_ADA_COMMENTLINE);
+
+ while (!sc.atLineEnd) {
+ sc.Forward();
+ }
+}
+
+static void ColouriseDelimiter(StyleContext& sc, bool& apostropheStartsAttribute) {
+ apostropheStartsAttribute = sc.Match (')');
+ sc.SetState(SCE_ADA_DELIMITER);
+ sc.ForwardSetState(SCE_ADA_DEFAULT);
+}
+
+static void ColouriseLabel(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute) {
+ apostropheStartsAttribute = false;
+
+ sc.SetState(SCE_ADA_LABEL);
+
+ // Skip "<<"
+ sc.Forward();
+ sc.Forward();
+
+ SString identifier;
+
+ while (!sc.atLineEnd && !IsSeparatorOrDelimiterCharacter(sc.ch)) {
+ identifier += static_cast<char>(tolower(sc.ch));
+ sc.Forward();
+ }
+
+ // Skip ">>"
+ if (sc.Match('>', '>')) {
+ sc.Forward();
+ sc.Forward();
+ } else {
+ sc.ChangeState(SCE_ADA_ILLEGAL);
+ }
+
+ // If the name is an invalid identifier or a keyword, then make it invalid label
+ if (!IsValidIdentifier(identifier) || keywords.InList(identifier.c_str())) {
+ sc.ChangeState(SCE_ADA_ILLEGAL);
+ }
+
+ sc.SetState(SCE_ADA_DEFAULT);
+
+}
+
+static void ColouriseNumber(StyleContext& sc, bool& apostropheStartsAttribute) {
+ apostropheStartsAttribute = true;
+
+ SString number;
+ sc.SetState(SCE_ADA_NUMBER);
+
+ // Get all characters up to a delimiter or a separator, including points, but excluding
+ // double points (ranges).
+ while (!IsSeparatorOrDelimiterCharacter(sc.ch) || (sc.ch == '.' && sc.chNext != '.')) {
+ number += static_cast<char>(sc.ch);
+ sc.Forward();
+ }
+
+ // Special case: exponent with sign
+ if ((sc.chPrev == 'e' || sc.chPrev == 'E') &&
+ (sc.ch == '+' || sc.ch == '-')) {
+ number += static_cast<char>(sc.ch);
+ sc.Forward ();
+
+ while (!IsSeparatorOrDelimiterCharacter(sc.ch)) {
+ number += static_cast<char>(sc.ch);
+ sc.Forward();
+ }
+ }
+
+ if (!IsValidNumber(number)) {
+ sc.ChangeState(SCE_ADA_ILLEGAL);
+ }
+
+ sc.SetState(SCE_ADA_DEFAULT);
+}
+
+static void ColouriseString(StyleContext& sc, bool& apostropheStartsAttribute) {
+ apostropheStartsAttribute = true;
+
+ sc.SetState(SCE_ADA_STRING);
+ sc.Forward();
+
+ ColouriseContext(sc, '"', SCE_ADA_STRINGEOL);
+}
+
+static void ColouriseWhiteSpace(StyleContext& sc, bool& /*apostropheStartsAttribute*/) {
+ // Apostrophe meaning is not changed, but the parameter is present for uniformity
+ sc.SetState(SCE_ADA_DEFAULT);
+ sc.ForwardSetState(SCE_ADA_DEFAULT);
+}
+
+static void ColouriseWord(StyleContext& sc, WordList& keywords, bool& apostropheStartsAttribute) {
+ apostropheStartsAttribute = true;
+ sc.SetState(SCE_ADA_IDENTIFIER);
+
+ SString word;
+
+ while (!sc.atLineEnd && !IsSeparatorOrDelimiterCharacter(sc.ch)) {
+ word += static_cast<char>(tolower(sc.ch));
+ sc.Forward();
+ }
+
+ if (!IsValidIdentifier(word)) {
+ sc.ChangeState(SCE_ADA_ILLEGAL);
+
+ } else if (keywords.InList(word.c_str())) {
+ sc.ChangeState(SCE_ADA_WORD);
+
+ if (word != "all") {
+ apostropheStartsAttribute = false;
+ }
+ }
+
+ sc.SetState(SCE_ADA_DEFAULT);
+}
+
+//
+// ColouriseDocument
+//
+
+static void ColouriseDocument(
+ unsigned int startPos,
+ int length,
+ int initStyle,
+ WordList *keywordlists[],
+ Accessor &styler) {
+ WordList &keywords = *keywordlists[0];
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ int lineCurrent = styler.GetLine(startPos);
+ bool apostropheStartsAttribute = (styler.GetLineState(lineCurrent) & 1) != 0;
+
+ while (sc.More()) {
+ if (sc.atLineEnd) {
+ // Go to the next line
+ sc.Forward();
+ lineCurrent++;
+
+ // Remember the line state for future incremental lexing
+ styler.SetLineState(lineCurrent, apostropheStartsAttribute);
+
+ // Don't continue any styles on the next line
+ sc.SetState(SCE_ADA_DEFAULT);
+ }
+
+ // Comments
+ if (sc.Match('-', '-')) {
+ ColouriseComment(sc, apostropheStartsAttribute);
+
+ // Strings
+ } else if (sc.Match('"')) {
+ ColouriseString(sc, apostropheStartsAttribute);
+
+ // Characters
+ } else if (sc.Match('\'') && !apostropheStartsAttribute) {
+ ColouriseCharacter(sc, apostropheStartsAttribute);
+
+ // Labels
+ } else if (sc.Match('<', '<')) {
+ ColouriseLabel(sc, keywords, apostropheStartsAttribute);
+
+ // Whitespace
+ } else if (IsASpace(sc.ch)) {
+ ColouriseWhiteSpace(sc, apostropheStartsAttribute);
+
+ // Delimiters
+ } else if (IsDelimiterCharacter(sc.ch)) {
+ ColouriseDelimiter(sc, apostropheStartsAttribute);
+
+ // Numbers
+ } else if (IsADigit(sc.ch) || sc.ch == '#') {
+ ColouriseNumber(sc, apostropheStartsAttribute);
+
+ // Keywords or identifiers
+ } else {
+ ColouriseWord(sc, keywords, apostropheStartsAttribute);
+ }
+ }
+
+ sc.Complete();
+}
+
+static inline bool IsDelimiterCharacter(int ch) {
+ switch (ch) {
+ case '&':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case ',':
+ case '-':
+ case '.':
+ case '/':
+ case ':':
+ case ';':
+ case '<':
+ case '=':
+ case '>':
+ case '|':
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline bool IsNumberCharacter(int ch) {
+ return IsNumberStartCharacter(ch) ||
+ ch == '_' ||
+ ch == '.' ||
+ ch == '#' ||
+ (ch >= 'a' && ch <= 'f') ||
+ (ch >= 'A' && ch <= 'F');
+}
+
+static inline bool IsNumberStartCharacter(int ch) {
+ return IsADigit(ch);
+}
+
+static inline bool IsSeparatorOrDelimiterCharacter(int ch) {
+ return IsASpace(ch) || IsDelimiterCharacter(ch);
+}
+
+static bool IsValidIdentifier(const SString& identifier) {
+ // First character can't be '_', so initialize the flag to true
+ bool lastWasUnderscore = true;
+
+ size_t length = identifier.length();
+
+ // Zero-length identifiers are not valid (these can occur inside labels)
+ if (length == 0) {
+ return false;
+ }
+
+ // Check for valid character at the start
+ if (!IsWordStartCharacter(identifier[0])) {
+ return false;
+ }
+
+ // Check for only valid characters and no double underscores
+ for (size_t i = 0; i < length; i++) {
+ if (!IsWordCharacter(identifier[i]) ||
+ (identifier[i] == '_' && lastWasUnderscore)) {
+ return false;
+ }
+ lastWasUnderscore = identifier[i] == '_';
+ }
+
+ // Check for underscore at the end
+ if (lastWasUnderscore == true) {
+ return false;
+ }
+
+ // All checks passed
+ return true;
+}
+
+static bool IsValidNumber(const SString& number) {
+ int hashPos = number.search("#");
+ bool seenDot = false;
+
+ size_t i = 0;
+ size_t length = number.length();
+
+ if (length == 0)
+ return false; // Just in case
+
+ // Decimal number
+ if (hashPos == -1) {
+ bool canBeSpecial = false;
+
+ for (; i < length; i++) {
+ if (number[i] == '_') {
+ if (!canBeSpecial) {
+ return false;
+ }
+ canBeSpecial = false;
+ } else if (number[i] == '.') {
+ if (!canBeSpecial || seenDot) {
+ return false;
+ }
+ canBeSpecial = false;
+ seenDot = true;
+ } else if (IsADigit(number[i])) {
+ canBeSpecial = true;
+ } else {
+ break;
+ }
+ }
+
+ if (!canBeSpecial)
+ return false;
+ } else {
+ // Based number
+ bool canBeSpecial = false;
+ int base = 0;
+
+ // Parse base
+ for (; i < length; i++) {
+ int ch = number[i];
+ if (ch == '_') {
+ if (!canBeSpecial)
+ return false;
+ canBeSpecial = false;
+ } else if (IsADigit(ch)) {
+ base = base * 10 + (ch - '0');
+ if (base > 16)
+ return false;
+ canBeSpecial = true;
+ } else if (ch == '#' && canBeSpecial) {
+ break;
+ } else {
+ return false;
+ }
+ }
+
+ if (base < 2)
+ return false;
+ if (i == length)
+ return false;
+
+ i++; // Skip over '#'
+
+ // Parse number
+ canBeSpecial = false;
+
+ for (; i < length; i++) {
+ int ch = tolower(number[i]);
+
+ if (ch == '_') {
+ if (!canBeSpecial) {
+ return false;
+ }
+ canBeSpecial = false;
+
+ } else if (ch == '.') {
+ if (!canBeSpecial || seenDot) {
+ return false;
+ }
+ canBeSpecial = false;
+ seenDot = true;
+
+ } else if (IsADigit(ch)) {
+ if (ch - '0' >= base) {
+ return false;
+ }
+ canBeSpecial = true;
+
+ } else if (ch >= 'a' && ch <= 'f') {
+ if (ch - 'a' + 10 >= base) {
+ return false;
+ }
+ canBeSpecial = true;
+
+ } else if (ch == '#' && canBeSpecial) {
+ break;
+
+ } else {
+ return false;
+ }
+ }
+
+ if (i == length) {
+ return false;
+ }
+
+ i++;
+ }
+
+ // Exponent (optional)
+ if (i < length) {
+ if (number[i] != 'e' && number[i] != 'E')
+ return false;
+
+ i++; // Move past 'E'
+
+ if (i == length) {
+ return false;
+ }
+
+ if (number[i] == '+')
+ i++;
+ else if (number[i] == '-') {
+ if (seenDot) {
+ i++;
+ } else {
+ return false; // Integer literals should not have negative exponents
+ }
+ }
+
+ if (i == length) {
+ return false;
+ }
+
+ bool canBeSpecial = false;
+
+ for (; i < length; i++) {
+ if (number[i] == '_') {
+ if (!canBeSpecial) {
+ return false;
+ }
+ canBeSpecial = false;
+ } else if (IsADigit(number[i])) {
+ canBeSpecial = true;
+ } else {
+ return false;
+ }
+ }
+
+ if (!canBeSpecial)
+ return false;
+ }
+
+ // if i == length, number was parsed successfully.
+ return i == length;
+}
+
+static inline bool IsWordCharacter(int ch) {
+ return IsWordStartCharacter(ch) || IsADigit(ch);
+}
+
+static inline bool IsWordStartCharacter(int ch) {
+ return (isascii(ch) && isalpha(ch)) || ch == '_';
+}
diff --git a/src/LexAsm.cpp b/src/LexAsm.cpp
new file mode 100755
index 0000000..93e0b37
--- /dev/null
+++ b/src/LexAsm.cpp
@@ -0,0 +1,177 @@
+// Scintilla source code edit control
+/** @file LexAsm.cxx
+ ** Lexer for Assembler, just for the MASM syntax
+ ** Written by The Black Horus
+ ** Enhancements and NASM stuff by Kein-Hong Man, 2003-10
+ ** SCE_ASM_COMMENTBLOCK and SCE_ASM_CHARACTER are for future GNU as colouring
+ **/
+// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+
+static inline bool IsAWordChar(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '.' ||
+ ch == '_' || ch == '?');
+}
+
+static inline bool IsAWordStart(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '.' ||
+ ch == '%' || ch == '@' || ch == '$' || ch == '?');
+}
+
+static inline bool IsAsmOperator(char ch) {
+ if (isalnum(ch))
+ return false;
+ // '.' left out as it is used to make up numbers
+ if (ch == '*' || ch == '/' || ch == '-' || ch == '+' ||
+ ch == '(' || ch == ')' || ch == '=' || ch == '^' ||
+ ch == '[' || ch == ']' || ch == '<' || ch == '&' ||
+ ch == '>' || ch == ',' || ch == '|' || ch == '~' ||
+ ch == '%' || ch == ':')
+ return true;
+ return false;
+}
+
+static void ColouriseAsmDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+ Accessor &styler) {
+
+ WordList &cpuInstruction = *keywordlists[0];
+ WordList &mathInstruction = *keywordlists[1];
+ WordList &registers = *keywordlists[2];
+ WordList &directive = *keywordlists[3];
+ WordList &directiveOperand = *keywordlists[4];
+ WordList &extInstruction = *keywordlists[5];
+
+ // Do not leak onto next line
+ if (initStyle == SCE_ASM_STRINGEOL)
+ initStyle = SCE_ASM_DEFAULT;
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ for (; sc.More(); sc.Forward())
+ {
+
+ // Prevent SCE_ASM_STRINGEOL from leaking back to previous line
+ if (sc.atLineStart && (sc.state == SCE_ASM_STRING)) {
+ sc.SetState(SCE_ASM_STRING);
+ } else if (sc.atLineStart && (sc.state == SCE_ASM_CHARACTER)) {
+ sc.SetState(SCE_ASM_CHARACTER);
+ }
+
+ // Handle line continuation generically.
+ if (sc.ch == '\\') {
+ if (sc.chNext == '\n' || sc.chNext == '\r') {
+ sc.Forward();
+ if (sc.ch == '\r' && sc.chNext == '\n') {
+ sc.Forward();
+ }
+ continue;
+ }
+ }
+
+ // Determine if the current state should terminate.
+ if (sc.state == SCE_ASM_OPERATOR) {
+ if (!IsAsmOperator(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_ASM_DEFAULT);
+ }
+ }else if (sc.state == SCE_ASM_NUMBER) {
+ if (!IsAWordChar(sc.ch)) {
+ sc.SetState(SCE_ASM_DEFAULT);
+ }
+ } else if (sc.state == SCE_ASM_IDENTIFIER) {
+ if (!IsAWordChar(sc.ch) ) {
+ char s[100];
+ sc.GetCurrentLowered(s, sizeof(s));
+
+ if (cpuInstruction.InList(s)) {
+ sc.ChangeState(SCE_ASM_CPUINSTRUCTION);
+ } else if (mathInstruction.InList(s)) {
+ sc.ChangeState(SCE_ASM_MATHINSTRUCTION);
+ } else if (registers.InList(s)) {
+ sc.ChangeState(SCE_ASM_REGISTER);
+ } else if (directive.InList(s)) {
+ sc.ChangeState(SCE_ASM_DIRECTIVE);
+ } else if (directiveOperand.InList(s)) {
+ sc.ChangeState(SCE_ASM_DIRECTIVEOPERAND);
+ } else if (extInstruction.InList(s)) {
+ sc.ChangeState(SCE_ASM_EXTINSTRUCTION);
+ }
+ sc.SetState(SCE_ASM_DEFAULT);
+ }
+ }
+ else if (sc.state == SCE_ASM_COMMENT ) {
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_ASM_DEFAULT);
+ }
+ } else if (sc.state == SCE_ASM_STRING) {
+ if (sc.ch == '\\') {
+ if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
+ sc.Forward();
+ }
+ } else if (sc.ch == '\"') {
+ sc.ForwardSetState(SCE_ASM_DEFAULT);
+ } else if (sc.atLineEnd) {
+ sc.ChangeState(SCE_ASM_STRINGEOL);
+ sc.ForwardSetState(SCE_ASM_DEFAULT);
+ }
+ } else if (sc.state == SCE_ASM_CHARACTER) {
+ if (sc.ch == '\\') {
+ if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
+ sc.Forward();
+ }
+ } else if (sc.ch == '\'') {
+ sc.ForwardSetState(SCE_ASM_DEFAULT);
+ } else if (sc.atLineEnd) {
+ sc.ChangeState(SCE_ASM_STRINGEOL);
+ sc.ForwardSetState(SCE_ASM_DEFAULT);
+ }
+ }
+
+ // Determine if a new state should be entered.
+ if (sc.state == SCE_ASM_DEFAULT) {
+ if (sc.ch == ';'){
+ sc.SetState(SCE_ASM_COMMENT);
+ } else if (isdigit(sc.ch) || (sc.ch == '.' && isdigit(sc.chNext))) {
+ sc.SetState(SCE_ASM_NUMBER);
+ } else if (IsAWordStart(sc.ch)) {
+ sc.SetState(SCE_ASM_IDENTIFIER);
+ } else if (sc.ch == '\"') {
+ sc.SetState(SCE_ASM_STRING);
+ } else if (sc.ch == '\'') {
+ sc.SetState(SCE_ASM_CHARACTER);
+ } else if (IsAsmOperator(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_ASM_OPERATOR);
+ }
+ }
+
+ }
+ sc.Complete();
+}
+
+static const char * const asmWordListDesc[] = {
+ "CPU instructions",
+ "FPU instructions",
+ "Registers",
+ "Directives",
+ "Directive operands",
+ "Extended instructions",
+ 0
+};
+
+LexerModule lmAsm(SCLEX_ASM, ColouriseAsmDoc, "asm", 0, asmWordListDesc);
+
diff --git a/src/LexAsn1.cpp b/src/LexAsn1.cpp
new file mode 100755
index 0000000..1600e63
--- /dev/null
+++ b/src/LexAsn1.cpp
@@ -0,0 +1,181 @@
+// Scintilla source code edit control
+/** @file LexAsn1.cxx
+ ** Lexer for ASN.1
+ **/
+// Copyright 2004 by Herr Pfarrer rpfarrer <at> yahoo <dot> de
+// Last Updated: 20/07/2004
+// The License.txt file describes the conditions under which this software may be distributed.
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+// Some char test functions
+static bool isAsn1Number(int ch)
+{
+ return (ch >= '0' && ch <= '9');
+}
+
+static bool isAsn1Letter(int ch)
+{
+ return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
+}
+
+static bool isAsn1Char(int ch)
+{
+ return (ch == '-' ) || isAsn1Number(ch) || isAsn1Letter (ch);
+}
+
+//
+// Function determining the color of a given code portion
+// Based on a "state"
+//
+static void ColouriseAsn1Doc(unsigned int startPos, int length, int initStyle, WordList *keywordLists[], Accessor &styler)
+{
+ // The keywords
+ WordList &Keywords = *keywordLists[0];
+ WordList &Attributes = *keywordLists[1];
+ WordList &Descriptors = *keywordLists[2];
+ WordList &Types = *keywordLists[3];
+
+ // Parse the whole buffer character by character using StyleContext
+ StyleContext sc(startPos, length, initStyle, styler);
+ for (; sc.More(); sc.Forward())
+ {
+ // The state engine
+ switch (sc.state)
+ {
+ case SCE_ASN1_DEFAULT: // Plain characters
+asn1_default:
+ if (sc.ch == '-' && sc.chNext == '-')
+ // A comment begins here
+ sc.SetState(SCE_ASN1_COMMENT);
+ else if (sc.ch == '"')
+ // A string begins here
+ sc.SetState(SCE_ASN1_STRING);
+ else if (isAsn1Number (sc.ch))
+ // A number starts here (identifier should start with a letter in ASN.1)
+ sc.SetState(SCE_ASN1_SCALAR);
+ else if (isAsn1Char (sc.ch))
+ // An identifier starts here (identifier always start with a letter)
+ sc.SetState(SCE_ASN1_IDENTIFIER);
+ else if (sc.ch == ':')
+ // A ::= operator starts here
+ sc.SetState(SCE_ASN1_OPERATOR);
+ break;
+ case SCE_ASN1_COMMENT: // A comment
+ if (sc.ch == '\r' || sc.ch == '\n')
+ // A comment ends here
+ sc.SetState(SCE_ASN1_DEFAULT);
+ break;
+ case SCE_ASN1_IDENTIFIER: // An identifier (keyword, attribute, descriptor or type)
+ if (!isAsn1Char (sc.ch))
+ {
+ // The end of identifier is here: we can look for it in lists by now and change its state
+ char s[100];
+ sc.GetCurrent(s, sizeof(s));
+ if (Keywords.InList(s))
+ // It's a keyword, change its state
+ sc.ChangeState(SCE_ASN1_KEYWORD);
+ else if (Attributes.InList(s))
+ // It's an attribute, change its state
+ sc.ChangeState(SCE_ASN1_ATTRIBUTE);
+ else if (Descriptors.InList(s))
+ // It's a descriptor, change its state
+ sc.ChangeState(SCE_ASN1_DESCRIPTOR);
+ else if (Types.InList(s))
+ // It's a type, change its state
+ sc.ChangeState(SCE_ASN1_TYPE);
+
+ // Set to default now
+ sc.SetState(SCE_ASN1_DEFAULT);
+ }
+ break;
+ case SCE_ASN1_STRING: // A string delimited by ""
+ if (sc.ch == '"')
+ {
+ // A string ends here
+ sc.ForwardSetState(SCE_ASN1_DEFAULT);
+
+ // To correctly manage a char sticking to the string quote
+ goto asn1_default;
+ }
+ break;
+ case SCE_ASN1_SCALAR: // A plain number
+ if (!isAsn1Number (sc.ch))
+ // A number ends here
+ sc.SetState(SCE_ASN1_DEFAULT);
+ break;
+ case SCE_ASN1_OPERATOR: // The affectation operator ::= and wath follows (eg: ::= { org 6 } OID or ::= 12 trap)
+ if (sc.ch == '{')
+ {
+ // An OID definition starts here: enter the sub loop
+ for (; sc.More(); sc.Forward())
+ {
+ if (isAsn1Number (sc.ch) && (!isAsn1Char (sc.chPrev) || isAsn1Number (sc.chPrev)))
+ // The OID number is highlighted
+ sc.SetState(SCE_ASN1_OID);
+ else if (isAsn1Char (sc.ch))
+ // The OID parent identifier is plain
+ sc.SetState(SCE_ASN1_IDENTIFIER);
+ else
+ sc.SetState(SCE_ASN1_DEFAULT);
+
+ if (sc.ch == '}')
+ // Here ends the OID and the operator sub loop: go back to main loop
+ break;
+ }
+ }
+ else if (isAsn1Number (sc.ch))
+ {
+ // A trap number definition starts here: enter the sub loop
+ for (; sc.More(); sc.Forward())
+ {
+ if (isAsn1Number (sc.ch))
+ // The trap number is highlighted
+ sc.SetState(SCE_ASN1_OID);
+ else
+ {
+ // The number ends here: go back to main loop
+ sc.SetState(SCE_ASN1_DEFAULT);
+ break;
+ }
+ }
+ }
+ else if (sc.ch != ':' && sc.ch != '=' && sc.ch != ' ')
+ // The operator doesn't imply an OID definition nor a trap, back to main loop
+ goto asn1_default; // To be sure to handle actually the state change
+ break;
+ }
+ }
+ sc.Complete();
+}
+
+static void FoldAsn1Doc(unsigned int, int, int, WordList *[], Accessor &styler)
+{
+ // No folding enabled, no reason to continue...
+ if( styler.GetPropertyInt("fold") == 0 )
+ return;
+
+ // No folding implemented: doesn't make sense for ASN.1
+}
+
+static const char * const asn1WordLists[] = {
+ "Keywords",
+ "Attributes",
+ "Descriptors",
+ "Types",
+ 0, };
+
+
+LexerModule lmAns1(SCLEX_ASN1, ColouriseAsn1Doc, "asn1", FoldAsn1Doc, asn1WordLists);
diff --git a/src/LexBaan.cpp b/src/LexBaan.cpp
new file mode 100755
index 0000000..3a36eb8
--- /dev/null
+++ b/src/LexBaan.cpp
@@ -0,0 +1,189 @@
+// Scintilla source code edit control
+/** @file LexBaan.cxx
+ ** Lexer for Baan.
+ ** Based heavily on LexCPP.cxx
+ **/
+// Copyright 2001- by Vamsi Potluru <vamsi@who.net> & Praveen Ambekar <ambekarpraveen@yahoo.com>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+static inline bool IsAWordChar(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_' || ch == '$' || ch == ':');
+}
+
+static inline bool IsAWordStart(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '_');
+}
+
+static void ColouriseBaanDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+ Accessor &styler) {
+
+ WordList &keywords = *keywordlists[0];
+ WordList &keywords2 = *keywordlists[1];
+ bool stylingWithinPreprocessor = styler.GetPropertyInt("styling.within.preprocessor") != 0;
+
+ if (initStyle == SCE_BAAN_STRINGEOL) // Does not leak onto next line
+ initStyle = SCE_BAAN_DEFAULT;
+
+ int visibleChars = 0;
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ for (; sc.More(); sc.Forward()) {
+
+ if (sc.state == SCE_BAAN_OPERATOR) {
+ sc.SetState(SCE_BAAN_DEFAULT);
+ } else if (sc.state == SCE_BAAN_NUMBER) {
+ if (!IsAWordChar(sc.ch)) {
+ sc.SetState(SCE_BAAN_DEFAULT);
+ }
+ } else if (sc.state == SCE_BAAN_IDENTIFIER) {
+ if (!IsAWordChar(sc.ch)) {
+ char s[100];
+ sc.GetCurrentLowered(s, sizeof(s));
+ if (keywords.InList(s)) {
+ sc.ChangeState(SCE_BAAN_WORD);
+ } else if (keywords2.InList(s)) {
+ sc.ChangeState(SCE_BAAN_WORD2);
+ }
+ sc.SetState(SCE_BAAN_DEFAULT);
+ }
+ } else if (sc.state == SCE_BAAN_PREPROCESSOR) {
+ if (stylingWithinPreprocessor) {
+ if (IsASpace(sc.ch)) {
+ sc.SetState(SCE_BAAN_DEFAULT);
+ }
+ } else {
+ if (sc.atLineEnd && (sc.chNext != '^')) {
+ sc.SetState(SCE_BAAN_DEFAULT);
+ }
+ }
+ } else if (sc.state == SCE_BAAN_COMMENT) {
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_BAAN_DEFAULT);
+ }
+ } else if (sc.state == SCE_BAAN_COMMENTDOC) {
+ if (sc.MatchIgnoreCase("enddllusage")) {
+ for (unsigned int i = 0; i < 10; i++){
+ sc.Forward();
+ }
+ sc.ForwardSetState(SCE_BAAN_DEFAULT);
+ }
+ } else if (sc.state == SCE_BAAN_STRING) {
+ if (sc.ch == '\"') {
+ sc.ForwardSetState(SCE_BAAN_DEFAULT);
+ } else if ((sc.atLineEnd) && (sc.chNext != '^')) {
+ sc.ChangeState(SCE_BAAN_STRINGEOL);
+ sc.ForwardSetState(SCE_C_DEFAULT);
+ visibleChars = 0;
+ }
+ }
+
+ if (sc.state == SCE_BAAN_DEFAULT) {
+ if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
+ sc.SetState(SCE_BAAN_NUMBER);
+ } else if (sc.MatchIgnoreCase("dllusage")){
+ sc.SetState(SCE_BAAN_COMMENTDOC);
+ do {
+ sc.Forward();
+ } while ((!sc.atLineEnd) && sc.More());
+ } else if (IsAWordStart(sc.ch)) {
+ sc.SetState(SCE_BAAN_IDENTIFIER);
+ } else if (sc.Match('|')){
+ sc.SetState(SCE_BAAN_COMMENT);
+ } else if (sc.ch == '\"') {
+ sc.SetState(SCE_BAAN_STRING);
+ } else if (sc.ch == '#' && visibleChars == 0) {
+ // Preprocessor commands are alone on their line
+ sc.SetState(SCE_BAAN_PREPROCESSOR);
+ // Skip whitespace between # and preprocessor word
+ do {
+ sc.Forward();
+ } while (IsASpace(sc.ch) && sc.More());
+ } else if (isoperator(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_BAAN_OPERATOR);
+ }
+ }
+ if (sc.atLineEnd) {
+ // Reset states to begining of colourise so no surprises
+ // if different sets of lines lexed.
+ visibleChars = 0;
+ }
+ if (!IsASpace(sc.ch)) {
+ visibleChars++;
+ }
+ }
+ sc.Complete();
+}
+
+static void FoldBaanDoc(unsigned int startPos, int length, int initStyle, WordList *[],
+ Accessor &styler) {
+ bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ unsigned int endPos = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
+ int levelCurrent = levelPrev;
+ char chNext = styler[startPos];
+ int styleNext = styler.StyleAt(startPos);
+ int style = initStyle;
+ for (unsigned int i = startPos; i < endPos; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ int stylePrev = style;
+ style = styleNext;
+ styleNext = styler.StyleAt(i + 1);
+ bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
+ if (foldComment &&
+ (style == SCE_BAAN_COMMENT || style == SCE_BAAN_COMMENTDOC)) {
+ if (style != stylePrev) {
+ levelCurrent++;
+ } else if ((style != styleNext) && !atEOL) {
+ // Comments don't end at end of line and the next character may be unstyled.
+ levelCurrent--;
+ }
+ }
+ if (style == SCE_BAAN_OPERATOR) {
+ if (ch == '{') {
+ levelCurrent++;
+ } else if (ch == '}') {
+ levelCurrent--;
+ }
+ }
+ if (atEOL) {
+ int lev = levelPrev;
+ if (visibleChars == 0 && foldCompact)
+ 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);
+}
+
+LexerModule lmBaan(SCLEX_BAAN, ColouriseBaanDoc, "baan", FoldBaanDoc);
diff --git a/src/LexBash.cpp b/src/LexBash.cpp
new file mode 100755
index 0000000..e9c31d6
--- /dev/null
+++ b/src/LexBash.cpp
@@ -0,0 +1,663 @@
+// Scintilla source code edit control
+/** @file LexBash.cxx
+ ** Lexer for Bash.
+ **/
+// Copyright 2004-2005 by Neil Hodgson <neilh@scintilla.org>
+// Adapted from LexPerl by Kein-Hong Man <mkh@pl.jaring.my> 2004
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+#define BASH_BASE_ERROR 65
+#define BASH_BASE_DECIMAL 66
+#define BASH_BASE_HEX 67
+#define BASH_BASE_OCTAL 68
+#define BASH_BASE_OCTAL_ERROR 69
+
+#define HERE_DELIM_MAX 256
+
+static inline int translateBashDigit(char ch) {
+ if (ch >= '0' && ch <= '9') {
+ return ch - '0';
+ } else if (ch >= 'a' && ch <= 'z') {
+ return ch - 'a' + 10;
+ } else if (ch >= 'A' && ch <= 'Z') {
+ return ch - 'A' + 36;
+ } else if (ch == '@') {
+ return 62;
+ } else if (ch == '_') {
+ return 63;
+ }
+ return BASH_BASE_ERROR;
+}
+
+static inline bool isEOLChar(char ch) {
+ return (ch == '\r') || (ch == '\n');
+}
+
+static bool isSingleCharOp(char ch) {
+ char strCharSet[2];
+ strCharSet[0] = ch;
+ strCharSet[1] = '\0';
+ return (NULL != strstr("rwxoRWXOezsfdlpSbctugkTBMACahGLNn", strCharSet));
+}
+
+static inline bool isBashOperator(char ch) {
+ 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 == '~' ||
+ ch == '@')
+ return true;
+ return false;
+}
+
+static int classifyWordBash(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler) {
+ char s[100];
+ for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) {
+ s[i] = styler[start + i];
+ s[i + 1] = '\0';
+ }
+ char chAttr = SCE_SH_IDENTIFIER;
+ if (keywords.InList(s))
+ chAttr = SCE_SH_WORD;
+ styler.ColourTo(end, chAttr);
+ return chAttr;
+}
+
+static inline int getBashNumberBase(unsigned int start, unsigned int end, Accessor &styler) {
+ int base = 0;
+ for (unsigned int i = 0; i < end - start + 1 && i < 10; i++) {
+ base = base * 10 + (styler[start + i] - '0');
+ }
+ if (base > 64 || (end - start) > 1) {
+ return BASH_BASE_ERROR;
+ }
+ return base;
+}
+
+static inline bool isEndVar(char ch) {
+ return !isalnum(ch) && ch != '$' && ch != '_';
+}
+
+static inline bool isNonQuote(char ch) {
+ return isalnum(ch) || ch == '_';
+}
+
+static bool isMatch(Accessor &styler, int lengthDoc, int pos, const char *val) {
+ if ((pos + static_cast<int>(strlen(val))) >= lengthDoc) {
+ return false;
+ }
+ while (*val) {
+ if (*val != styler[pos++]) {
+ return false;
+ }
+ val++;
+ }
+ return true;
+}
+
+static char opposite(char ch) {
+ if (ch == '(')
+ return ')';
+ if (ch == '[')
+ return ']';
+ if (ch == '{')
+ return '}';
+ if (ch == '<')
+ return '>';
+ return ch;
+}
+
+static void ColouriseBashDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler) {
+
+ // Lexer for bash often has to backtrack to start of current style to determine
+ // which characters are being used as quotes, how deeply nested is the
+ // start position and what the termination string is for here documents
+
+ WordList &keywords = *keywordlists[0];
+
+ class HereDocCls {
+ public:
+ int State; // 0: '<<' encountered
+ // 1: collect the delimiter
+ // 2: here doc text (lines after the delimiter)
+ char Quote; // the char after '<<'
+ bool Quoted; // true if Quote in ('\'','"','`')
+ bool Indent; // indented delimiter (for <<-)
+ int DelimiterLength; // strlen(Delimiter)
+ char *Delimiter; // the Delimiter, 256: sizeof PL_tokenbuf
+ HereDocCls() {
+ State = 0;
+ Quote = 0;
+ Quoted = false;
+ Indent = 0;
+ DelimiterLength = 0;
+ Delimiter = new char[HERE_DELIM_MAX];
+ Delimiter[0] = '\0';
+ }
+ ~HereDocCls() {
+ delete []Delimiter;
+ }
+ };
+ HereDocCls HereDoc;
+
+ class QuoteCls {
+ public:
+ int Rep;
+ int Count;
+ char Up;
+ char Down;
+ QuoteCls() {
+ this->New(1);
+ }
+ void New(int r) {
+ Rep = r;
+ Count = 0;
+ Up = '\0';
+ Down = '\0';
+ }
+ void Open(char u) {
+ Count++;
+ Up = u;
+ Down = opposite(Up);
+ }
+ };
+ QuoteCls Quote;
+
+ int state = initStyle;
+ int numBase = 0;
+ unsigned int lengthDoc = startPos + length;
+
+ // If in a long distance lexical state, seek to the beginning to find quote characters
+ // Bash strings can be multi-line with embedded newlines, so backtrack.
+ // Bash numbers have additional state during lexing, so backtrack too.
+ if (state == SCE_SH_HERE_Q) {
+ while ((startPos > 1) && (styler.StyleAt(startPos) != SCE_SH_HERE_DELIM)) {
+ startPos--;
+ }
+ startPos = styler.LineStart(styler.GetLine(startPos));
+ state = styler.StyleAt(startPos - 1);
+ }
+ if (state == SCE_SH_STRING
+ || state == SCE_SH_BACKTICKS
+ || state == SCE_SH_CHARACTER
+ || state == SCE_SH_NUMBER
+ || state == SCE_SH_IDENTIFIER
+ || state == SCE_SH_COMMENTLINE
+ ) {
+ while ((startPos > 1) && (styler.StyleAt(startPos - 1) == state)) {
+ startPos--;
+ }
+ state = SCE_SH_DEFAULT;
+ }
+
+ styler.StartAt(startPos);
+ char chPrev = styler.SafeGetCharAt(startPos - 1);
+ if (startPos == 0)
+ chPrev = '\n';
+ char chNext = styler[startPos];
+ styler.StartSegment(startPos);
+
+ for (unsigned int i = startPos; i < lengthDoc; i++) {
+ char ch = chNext;
+ // if the current character is not consumed due to the completion of an
+ // earlier style, lexing can be restarted via a simple goto
+ restartLexer:
+ chNext = styler.SafeGetCharAt(i + 1);
+ char chNext2 = styler.SafeGetCharAt(i + 2);
+
+ if (styler.IsLeadByte(ch)) {
+ chNext = styler.SafeGetCharAt(i + 2);
+ chPrev = ' ';
+ i += 1;
+ continue;
+ }
+
+ if ((chPrev == '\r' && ch == '\n')) { // skip on DOS/Windows
+ styler.ColourTo(i, state);
+ chPrev = ch;
+ continue;
+ }
+
+ if (HereDoc.State == 1 && isEOLChar(ch)) {
+ // Begin of here-doc (the line after the here-doc delimiter):
+ // Lexically, the here-doc starts from the next line after the >>, but the
+ // first line of here-doc seem to follow the style of the last EOL sequence
+ HereDoc.State = 2;
+ if (HereDoc.Quoted) {
+ if (state == SCE_SH_HERE_DELIM) {
+ // Missing quote at end of string! We are stricter than bash.
+ // Colour here-doc anyway while marking this bit as an error.
+ state = SCE_SH_ERROR;
+ }
+ styler.ColourTo(i - 1, state);
+ // HereDoc.Quote always == '\''
+ state = SCE_SH_HERE_Q;
+ } else {
+ styler.ColourTo(i - 1, state);
+ // always switch
+ state = SCE_SH_HERE_Q;
+ }
+ }
+
+ if (state == SCE_SH_DEFAULT) {
+ if (ch == '\\') { // escaped character
+ if (i < lengthDoc - 1)
+ i++;
+ ch = chNext;
+ chNext = chNext2;
+ styler.ColourTo(i, SCE_SH_IDENTIFIER);
+ } else if (isdigit(ch)) {
+ state = SCE_SH_NUMBER;
+ numBase = BASH_BASE_DECIMAL;
+ if (ch == '0') { // hex,octal
+ if (chNext == 'x' || chNext == 'X') {
+ numBase = BASH_BASE_HEX;
+ i++;
+ ch = chNext;
+ chNext = chNext2;
+ } else if (isdigit(chNext)) {
+ numBase = BASH_BASE_OCTAL;
+ }
+ }
+ } else if (iswordstart(ch)) {
+ state = SCE_SH_WORD;
+ if (!iswordchar(chNext) && chNext != '+' && chNext != '-') {
+ // We need that if length of word == 1!
+ // This test is copied from the SCE_SH_WORD handler.
+ classifyWordBash(styler.GetStartSegment(), i, keywords, styler);
+ state = SCE_SH_DEFAULT;
+ }
+ } else if (ch == '#') {
+ state = SCE_SH_COMMENTLINE;
+ } else if (ch == '\"') {
+ state = SCE_SH_STRING;
+ Quote.New(1);
+ Quote.Open(ch);
+ } else if (ch == '\'') {
+ state = SCE_SH_CHARACTER;
+ Quote.New(1);
+ Quote.Open(ch);
+ } else if (ch == '`') {
+ state = SCE_SH_BACKTICKS;
+ Quote.New(1);
+ Quote.Open(ch);
+ } else if (ch == '$') {
+ if (chNext == '{') {
+ state = SCE_SH_PARAM;
+ goto startQuote;
+ } else if (chNext == '\'') {
+ state = SCE_SH_CHARACTER;
+ goto startQuote;
+ } else if (chNext == '"') {
+ state = SCE_SH_STRING;
+ goto startQuote;
+ } else if (chNext == '(' && chNext2 == '(') {
+ styler.ColourTo(i, SCE_SH_OPERATOR);
+ state = SCE_SH_DEFAULT;
+ goto skipChar;
+ } else if (chNext == '(' || chNext == '`') {
+ state = SCE_SH_BACKTICKS;
+ startQuote:
+ Quote.New(1);
+ Quote.Open(chNext);
+ goto skipChar;
+ } else {
+ state = SCE_SH_SCALAR;
+ skipChar:
+ i++;
+ ch = chNext;
+ chNext = chNext2;
+ }
+ } else if (ch == '*') {
+ if (chNext == '*') { // exponentiation
+ i++;
+ ch = chNext;
+ chNext = chNext2;
+ }
+ styler.ColourTo(i, SCE_SH_OPERATOR);
+ } else if (ch == '<' && chNext == '<') {
+ state = SCE_SH_HERE_DELIM;
+ HereDoc.State = 0;
+ HereDoc.Indent = false;
+ } else if (ch == '-' // file test operators
+ && isSingleCharOp(chNext)
+ && !isalnum((chNext2 = styler.SafeGetCharAt(i+2)))) {
+ styler.ColourTo(i + 1, SCE_SH_WORD);
+ state = SCE_SH_DEFAULT;
+ i++;
+ ch = chNext;
+ chNext = chNext2;
+ } else if (isBashOperator(ch)) {
+ styler.ColourTo(i, SCE_SH_OPERATOR);
+ } else {
+ // keep colouring defaults to make restart easier
+ styler.ColourTo(i, SCE_SH_DEFAULT);
+ }
+ } else if (state == SCE_SH_NUMBER) {
+ int digit = translateBashDigit(ch);
+ if (numBase == BASH_BASE_DECIMAL) {
+ if (ch == '#') {
+ numBase = getBashNumberBase(styler.GetStartSegment(), i - 1, styler);
+ if (numBase == BASH_BASE_ERROR) // take the rest as comment
+ goto numAtEnd;
+ } else if (!isdigit(ch))
+ goto numAtEnd;
+ } else if (numBase == BASH_BASE_HEX) {
+ if ((digit < 16) || (digit >= 36 && digit <= 41)) {
+ // hex digit 0-9a-fA-F
+ } else
+ goto numAtEnd;
+ } else if (numBase == BASH_BASE_OCTAL ||
+ numBase == BASH_BASE_OCTAL_ERROR) {
+ if (digit > 7) {
+ if (digit <= 9) {
+ numBase = BASH_BASE_OCTAL_ERROR;
+ } else
+ goto numAtEnd;
+ }
+ } else if (numBase == BASH_BASE_ERROR) {
+ if (digit > 9)
+ goto numAtEnd;
+ } else { // DD#DDDD number style handling
+ if (digit != BASH_BASE_ERROR) {
+ if (numBase <= 36) {
+ // case-insensitive if base<=36
+ if (digit >= 36) digit -= 26;
+ }
+ if (digit >= numBase) {
+ if (digit <= 9) {
+ numBase = BASH_BASE_ERROR;
+ } else
+ goto numAtEnd;
+ }
+ } else {
+ numAtEnd:
+ if (numBase == BASH_BASE_ERROR ||
+ numBase == BASH_BASE_OCTAL_ERROR)
+ state = SCE_SH_ERROR;
+ styler.ColourTo(i - 1, state);
+ state = SCE_SH_DEFAULT;
+ goto restartLexer;
+ }
+ }
+ } else if (state == SCE_SH_WORD) {
+ if (!iswordchar(chNext) && chNext != '+' && chNext != '-') {
+ // "." never used in Bash variable names
+ // but used in file names
+ classifyWordBash(styler.GetStartSegment(), i, keywords, styler);
+ state = SCE_SH_DEFAULT;
+ ch = ' ';
+ }
+ } else if (state == SCE_SH_IDENTIFIER) {
+ if (!iswordchar(chNext) && chNext != '+' && chNext != '-') {
+ styler.ColourTo(i, SCE_SH_IDENTIFIER);
+ state = SCE_SH_DEFAULT;
+ ch = ' ';
+ }
+ } else {
+ if (state == SCE_SH_COMMENTLINE) {
+ if (ch == '\\' && isEOLChar(chNext)) {
+ // comment continuation
+ if (chNext == '\r' && chNext2 == '\n') {
+ i += 2;
+ ch = styler.SafeGetCharAt(i);
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else {
+ i++;
+ ch = chNext;
+ chNext = chNext2;
+ }
+ } else if (isEOLChar(ch)) {
+ styler.ColourTo(i - 1, state);
+ state = SCE_SH_DEFAULT;
+ goto restartLexer;
+ } else if (isEOLChar(chNext)) {
+ styler.ColourTo(i, state);
+ state = SCE_SH_DEFAULT;
+ }
+ } else if (state == SCE_SH_HERE_DELIM) {
+ //
+ // From Bash info:
+ // ---------------
+ // Specifier format is: <<[-]WORD
+ // Optional '-' is for removal of leading tabs from here-doc.
+ // Whitespace acceptable after <<[-] operator
+ //
+ if (HereDoc.State == 0) { // '<<' encountered
+ HereDoc.State = 1;
+ HereDoc.Quote = chNext;
+ HereDoc.Quoted = false;
+ HereDoc.DelimiterLength = 0;
+ HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0';
+ if (chNext == '\'' || chNext == '\"') { // a quoted here-doc delimiter (' or ")
+ i++;
+ ch = chNext;
+ chNext = chNext2;
+ HereDoc.Quoted = true;
+ } else if (!HereDoc.Indent && chNext == '-') { // <<- indent case
+ HereDoc.Indent = true;
+ HereDoc.State = 0;
+ } else if (isalpha(chNext) || chNext == '_' || chNext == '\\'
+ || chNext == '-' || chNext == '+' || chNext == '!') {
+ // an unquoted here-doc delimiter, no special handling
+ // TODO check what exactly bash considers part of the delim
+ } else if (chNext == '<') { // HERE string <<<
+ i++;
+ ch = chNext;
+ chNext = chNext2;
+ styler.ColourTo(i, SCE_SH_HERE_DELIM);
+ state = SCE_SH_DEFAULT;
+ HereDoc.State = 0;
+ } else if (isspacechar(chNext)) {
+ // eat whitespace
+ HereDoc.State = 0;
+ } else if (isdigit(chNext) || chNext == '=' || chNext == '$') {
+ // left shift << or <<= operator cases
+ styler.ColourTo(i, SCE_SH_OPERATOR);
+ state = SCE_SH_DEFAULT;
+ HereDoc.State = 0;
+ } else {
+ // symbols terminates; deprecated zero-length delimiter
+ }
+ } else if (HereDoc.State == 1) { // collect the delimiter
+ if (HereDoc.Quoted) { // a quoted here-doc delimiter
+ if (ch == HereDoc.Quote) { // closing quote => end of delimiter
+ styler.ColourTo(i, state);
+ state = SCE_SH_DEFAULT;
+ } else {
+ if (ch == '\\' && chNext == HereDoc.Quote) { // escaped quote
+ i++;
+ ch = chNext;
+ chNext = chNext2;
+ }
+ HereDoc.Delimiter[HereDoc.DelimiterLength++] = ch;
+ HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0';
+ }
+ } else { // an unquoted here-doc delimiter
+ if (isalnum(ch) || ch == '_' || ch == '-' || ch == '+' || ch == '!') {
+ HereDoc.Delimiter[HereDoc.DelimiterLength++] = ch;
+ HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0';
+ } else if (ch == '\\') {
+ // skip escape prefix
+ } else {
+ styler.ColourTo(i - 1, state);
+ state = SCE_SH_DEFAULT;
+ goto restartLexer;
+ }
+ }
+ if (HereDoc.DelimiterLength >= HERE_DELIM_MAX - 1) {
+ styler.ColourTo(i - 1, state);
+ state = SCE_SH_ERROR;
+ goto restartLexer;
+ }
+ }
+ } else if (HereDoc.State == 2) {
+ // state == SCE_SH_HERE_Q
+ if (isMatch(styler, lengthDoc, i, HereDoc.Delimiter)) {
+ if (!HereDoc.Indent && isEOLChar(chPrev)) {
+ endHereDoc:
+ // standard HERE delimiter
+ i += HereDoc.DelimiterLength;
+ chPrev = styler.SafeGetCharAt(i - 1);
+ ch = styler.SafeGetCharAt(i);
+ if (isEOLChar(ch)) {
+ styler.ColourTo(i - 1, state);
+ state = SCE_SH_DEFAULT;
+ HereDoc.State = 0;
+ goto restartLexer;
+ }
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else if (HereDoc.Indent) {
+ // indented HERE delimiter
+ unsigned int bk = (i > 0)? i - 1: 0;
+ while (i > 0) {
+ ch = styler.SafeGetCharAt(bk--);
+ if (isEOLChar(ch)) {
+ goto endHereDoc;
+ } else if (!isspacechar(ch)) {
+ break; // got leading non-whitespace
+ }
+ }
+ }
+ }
+ } else if (state == SCE_SH_SCALAR) { // variable names
+ if (isEndVar(ch)) {
+ if ((state == SCE_SH_SCALAR)
+ && i == (styler.GetStartSegment() + 1)) {
+ // Special variable: $(, $_ etc.
+ styler.ColourTo(i, state);
+ state = SCE_SH_DEFAULT;
+ } else {
+ styler.ColourTo(i - 1, state);
+ state = SCE_SH_DEFAULT;
+ goto restartLexer;
+ }
+ }
+ } else if (state == SCE_SH_STRING
+ || state == SCE_SH_CHARACTER
+ || state == SCE_SH_BACKTICKS
+ || state == SCE_SH_PARAM
+ ) {
+ if (!Quote.Down && !isspacechar(ch)) {
+ Quote.Open(ch);
+ } else if (ch == '\\' && Quote.Up != '\\') {
+ i++;
+ ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else if (ch == Quote.Down) {
+ Quote.Count--;
+ if (Quote.Count == 0) {
+ Quote.Rep--;
+ if (Quote.Rep <= 0) {
+ styler.ColourTo(i, state);
+ state = SCE_SH_DEFAULT;
+ ch = ' ';
+ }
+ if (Quote.Up == Quote.Down) {
+ Quote.Count++;
+ }
+ }
+ } else if (ch == Quote.Up) {
+ Quote.Count++;
+ }
+ }
+ }
+ if (state == SCE_SH_ERROR) {
+ break;
+ }
+ chPrev = ch;
+ }
+ styler.ColourTo(lengthDoc - 1, state);
+}
+
+static bool IsCommentLine(int line, Accessor &styler) {
+ int pos = styler.LineStart(line);
+ int eol_pos = styler.LineStart(line + 1) - 1;
+ for (int i = pos; i < eol_pos; i++) {
+ char ch = styler[i];
+ if (ch == '#')
+ return true;
+ else if (ch != ' ' && ch != '\t')
+ return false;
+ }
+ return false;
+}
+
+static void FoldBashDoc(unsigned int startPos, int length, int, WordList *[],
+ Accessor &styler) {
+ bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ unsigned int endPos = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
+ int levelCurrent = levelPrev;
+ char chNext = styler[startPos];
+ int styleNext = styler.StyleAt(startPos);
+ for (unsigned int i = startPos; i < endPos; 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');
+ // Comment folding
+ if (foldComment && atEOL && IsCommentLine(lineCurrent, styler))
+ {
+ if (!IsCommentLine(lineCurrent - 1, styler)
+ && IsCommentLine(lineCurrent + 1, styler))
+ levelCurrent++;
+ else if (IsCommentLine(lineCurrent - 1, styler)
+ && !IsCommentLine(lineCurrent+1, styler))
+ levelCurrent--;
+ }
+ if (style == SCE_SH_OPERATOR) {
+ if (ch == '{') {
+ levelCurrent++;
+ } else if (ch == '}') {
+ levelCurrent--;
+ }
+ }
+ if (atEOL) {
+ int lev = levelPrev;
+ if (visibleChars == 0 && foldCompact)
+ 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 bashWordListDesc[] = {
+ "Keywords",
+ 0
+};
+
+LexerModule lmBash(SCLEX_BASH, ColouriseBashDoc, "bash", FoldBashDoc, bashWordListDesc);
diff --git a/src/LexBasic.cpp b/src/LexBasic.cpp
new file mode 100644
index 0000000..79ba2b8
--- /dev/null
+++ b/src/LexBasic.cpp
@@ -0,0 +1,369 @@
+// Scintilla source code edit control
+/** @file LexBasic.cxx
+ ** Lexer for BlitzBasic and PureBasic.
+ **/
+// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+// This tries to be a unified Lexer/Folder for all the BlitzBasic/BlitzMax/PurBasic basics
+// and derivatives. Once they diverge enough, might want to split it into multiple
+// lexers for more code clearity.
+//
+// Mail me (elias <at> users <dot> sf <dot> net) for any bugs.
+
+// Folding only works for simple things like functions or types.
+
+// You may want to have a look at my ctags lexer as well, if you additionally to coloring
+// and folding need to extract things like label tags in your editor.
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+/* Bits:
+ * 1 - whitespace
+ * 2 - operator
+ * 4 - identifier
+ * 8 - decimal digit
+ * 16 - hex digit
+ * 32 - bin digit
+ */
+static int character_classification[128] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 10, 2,
+ 60, 60, 28, 28, 28, 28, 28, 28, 28, 28, 2, 2, 2, 2, 2, 2,
+ 2, 20, 20, 20, 20, 20, 20, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 4,
+ 2, 20, 20, 20, 20, 20, 20, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2, 0
+};
+
+static bool IsSpace(int c) {
+ return c < 128 && (character_classification[c] & 1);
+}
+
+static bool IsOperator(int c) {
+ return c < 128 && (character_classification[c] & 2);
+}
+
+static bool IsIdentifier(int c) {
+ return c < 128 && (character_classification[c] & 4);
+}
+
+static bool IsDigit(int c) {
+ return c < 128 && (character_classification[c] & 8);
+}
+
+static bool IsHexDigit(int c) {
+ return c < 128 && (character_classification[c] & 16);
+}
+
+static bool IsBinDigit(int c) {
+ return c < 128 && (character_classification[c] & 32);
+}
+
+static int LowerCase(int c)
+{
+ if (c >= 'A' && c <= 'Z')
+ return 'a' + c - 'A';
+ return c;
+}
+
+static void ColouriseBasicDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler, char comment_char) {
+ bool wasfirst = true, isfirst = true; // true if first token in a line
+ styler.StartAt(startPos);
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ // Can't use sc.More() here else we miss the last character
+ for (; ; sc.Forward()) {
+ if (sc.state == SCE_B_IDENTIFIER) {
+ if (!IsIdentifier(sc.ch)) {
+ // Labels
+ if (wasfirst && sc.Match(':')) {
+ sc.ChangeState(SCE_B_LABEL);
+ sc.ForwardSetState(SCE_B_DEFAULT);
+ } else {
+ char s[100];
+ int kstates[4] = {
+ SCE_B_KEYWORD,
+ SCE_B_KEYWORD2,
+ SCE_B_KEYWORD3,
+ SCE_B_KEYWORD4,
+ };
+ sc.GetCurrentLowered(s, sizeof(s));
+ for (int i = 0; i < 4; i++) {
+ if (keywordlists[i]->InList(s)) {
+ sc.ChangeState(kstates[i]);
+ }
+ }
+ // Types, must set them as operator else they will be
+ // matched as number/constant
+ if (sc.Match('.') || sc.Match('$') || sc.Match('%') ||
+ sc.Match('#')) {
+ sc.SetState(SCE_B_OPERATOR);
+ } else {
+ sc.SetState(SCE_B_DEFAULT);
+ }
+ }
+ }
+ } else if (sc.state == SCE_B_OPERATOR) {
+ if (!IsOperator(sc.ch) || sc.Match('#'))
+ sc.SetState(SCE_B_DEFAULT);
+ } else if (sc.state == SCE_B_LABEL) {
+ if (!IsIdentifier(sc.ch))
+ sc.SetState(SCE_B_DEFAULT);
+ } else if (sc.state == SCE_B_CONSTANT) {
+ if (!IsIdentifier(sc.ch))
+ sc.SetState(SCE_B_DEFAULT);
+ } else if (sc.state == SCE_B_NUMBER) {
+ if (!IsDigit(sc.ch))
+ sc.SetState(SCE_B_DEFAULT);
+ } else if (sc.state == SCE_B_HEXNUMBER) {
+ if (!IsHexDigit(sc.ch))
+ sc.SetState(SCE_B_DEFAULT);
+ } else if (sc.state == SCE_B_BINNUMBER) {
+ if (!IsBinDigit(sc.ch))
+ sc.SetState(SCE_B_DEFAULT);
+ } else if (sc.state == SCE_B_STRING) {
+ if (sc.ch == '"') {
+ sc.ForwardSetState(SCE_B_DEFAULT);
+ }
+ if (sc.atLineEnd) {
+ sc.ChangeState(SCE_B_ERROR);
+ sc.SetState(SCE_B_DEFAULT);
+ }
+ } else if (sc.state == SCE_B_COMMENT || sc.state == SCE_B_PREPROCESSOR) {
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_B_DEFAULT);
+ }
+ }
+
+ if (sc.atLineStart)
+ isfirst = true;
+
+ if (sc.state == SCE_B_DEFAULT || sc.state == SCE_B_ERROR) {
+ if (isfirst && sc.Match('.')) {
+ sc.SetState(SCE_B_LABEL);
+ } else if (isfirst && sc.Match('#')) {
+ wasfirst = isfirst;
+ sc.SetState(SCE_B_IDENTIFIER);
+ } else if (sc.Match(comment_char)) {
+ // Hack to make deprecated QBASIC '$Include show
+ // up in freebasic with SCE_B_PREPROCESSOR.
+ if (comment_char == '\'' && sc.Match(comment_char, '$'))
+ sc.SetState(SCE_B_PREPROCESSOR);
+ else
+ sc.SetState(SCE_B_COMMENT);
+ } else if (sc.Match('"')) {
+ sc.SetState(SCE_B_STRING);
+ } else if (IsDigit(sc.ch)) {
+ sc.SetState(SCE_B_NUMBER);
+ } else if (sc.Match('$')) {
+ sc.SetState(SCE_B_HEXNUMBER);
+ } else if (sc.Match('%')) {
+ sc.SetState(SCE_B_BINNUMBER);
+ } else if (sc.Match('#')) {
+ sc.SetState(SCE_B_CONSTANT);
+ } else if (IsOperator(sc.ch)) {
+ sc.SetState(SCE_B_OPERATOR);
+ } else if (IsIdentifier(sc.ch)) {
+ wasfirst = isfirst;
+ sc.SetState(SCE_B_IDENTIFIER);
+ } else if (!IsSpace(sc.ch)) {
+ sc.SetState(SCE_B_ERROR);
+ }
+ }
+
+ if (!IsSpace(sc.ch))
+ isfirst = false;
+
+ if (!sc.More())
+ break;
+ }
+ sc.Complete();
+}
+
+static int CheckBlitzFoldPoint(char const *token, int &level) {
+ if (!strcmp(token, "function") ||
+ !strcmp(token, "type")) {
+ level |= SC_FOLDLEVELHEADERFLAG;
+ return 1;
+ }
+ if (!strcmp(token, "end function") ||
+ !strcmp(token, "end type")) {
+ return -1;
+ }
+ return 0;
+}
+
+static int CheckPureFoldPoint(char const *token, int &level) {
+ if (!strcmp(token, "procedure") ||
+ !strcmp(token, "enumeration") ||
+ !strcmp(token, "interface") ||
+ !strcmp(token, "structure")) {
+ level |= SC_FOLDLEVELHEADERFLAG;
+ return 1;
+ }
+ if (!strcmp(token, "endprocedure") ||
+ !strcmp(token, "endenumeration") ||
+ !strcmp(token, "endinterface") ||
+ !strcmp(token, "endstructure")) {
+ return -1;
+ }
+ return 0;
+}
+
+static int CheckFreeFoldPoint(char const *token, int &level) {
+ if (!strcmp(token, "function") ||
+ !strcmp(token, "sub") ||
+ !strcmp(token, "type")) {
+ level |= SC_FOLDLEVELHEADERFLAG;
+ return 1;
+ }
+ if (!strcmp(token, "end function") ||
+ !strcmp(token, "end sub") ||
+ !strcmp(token, "end type")) {
+ return -1;
+ }
+ return 0;
+}
+
+static void FoldBasicDoc(unsigned int startPos, int length,
+ Accessor &styler, int (*CheckFoldPoint)(char const *, int &)) {
+ int line = styler.GetLine(startPos);
+ int level = styler.LevelAt(line);
+ int go = 0, done = 0;
+ int endPos = startPos + length;
+ char word[256];
+ int wordlen = 0;
+ int i;
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ // Scan for tokens at the start of the line (they may include
+ // whitespace, for tokens like "End Function"
+ for (i = startPos; i < endPos; i++) {
+ int c = styler.SafeGetCharAt(i);
+ if (!done && !go) {
+ if (wordlen) { // are we scanning a token already?
+ word[wordlen] = static_cast<char>(LowerCase(c));
+ if (!IsIdentifier(c)) { // done with token
+ word[wordlen] = '\0';
+ go = CheckFoldPoint(word, level);
+ if (!go) {
+ // Treat any whitespace as single blank, for
+ // things like "End Function".
+ if (IsSpace(c) && IsIdentifier(word[wordlen - 1])) {
+ word[wordlen] = ' ';
+ if (wordlen < 255)
+ wordlen++;
+ }
+ else // done with this line
+ done = 1;
+ }
+ } else if (wordlen < 255) {
+ wordlen++;
+ }
+ } else { // start scanning at first non-whitespace character
+ if (!IsSpace(c)) {
+ if (IsIdentifier(c)) {
+ word[0] = static_cast<char>(LowerCase(c));
+ wordlen = 1;
+ } else // done with this line
+ done = 1;
+ }
+ }
+ }
+ if (c == '\n') { // line end
+ if (!done && wordlen == 0 && foldCompact) // line was only space
+ level |= SC_FOLDLEVELWHITEFLAG;
+ if (level != styler.LevelAt(line))
+ styler.SetLevel(line, level);
+ level += go;
+ line++;
+ // reset state
+ wordlen = 0;
+ level &= ~SC_FOLDLEVELHEADERFLAG;
+ level &= ~SC_FOLDLEVELWHITEFLAG;
+ go = 0;
+ done = 0;
+ }
+ }
+}
+
+static void ColouriseBlitzBasicDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler) {
+ ColouriseBasicDoc(startPos, length, initStyle, keywordlists, styler, ';');
+}
+
+static void ColourisePureBasicDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler) {
+ ColouriseBasicDoc(startPos, length, initStyle, keywordlists, styler, ';');
+}
+
+static void ColouriseFreeBasicDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler) {
+ ColouriseBasicDoc(startPos, length, initStyle, keywordlists, styler, '\'');
+}
+
+static void FoldBlitzBasicDoc(unsigned int startPos, int length, int,
+ WordList *[], Accessor &styler) {
+ FoldBasicDoc(startPos, length, styler, CheckBlitzFoldPoint);
+}
+
+static void FoldPureBasicDoc(unsigned int startPos, int length, int,
+ WordList *[], Accessor &styler) {
+ FoldBasicDoc(startPos, length, styler, CheckPureFoldPoint);
+}
+
+static void FoldFreeBasicDoc(unsigned int startPos, int length, int,
+ WordList *[], Accessor &styler) {
+ FoldBasicDoc(startPos, length, styler, CheckFreeFoldPoint);
+}
+
+static const char * const blitzbasicWordListDesc[] = {
+ "BlitzBasic Keywords",
+ "user1",
+ "user2",
+ "user3",
+ 0
+};
+
+static const char * const purebasicWordListDesc[] = {
+ "PureBasic Keywords",
+ "PureBasic PreProcessor Keywords",
+ "user defined 1",
+ "user defined 2",
+ 0
+};
+
+static const char * const freebasicWordListDesc[] = {
+ "FreeBasic Keywords",
+ "FreeBasic PreProcessor Keywords",
+ "user defined 1",
+ "user defined 2",
+ 0
+};
+
+LexerModule lmBlitzBasic(SCLEX_BLITZBASIC, ColouriseBlitzBasicDoc, "blitzbasic",
+ FoldBlitzBasicDoc, blitzbasicWordListDesc);
+
+LexerModule lmPureBasic(SCLEX_PUREBASIC, ColourisePureBasicDoc, "purebasic",
+ FoldPureBasicDoc, purebasicWordListDesc);
+
+LexerModule lmFreeBasic(SCLEX_FREEBASIC, ColouriseFreeBasicDoc, "freebasic",
+ FoldFreeBasicDoc, freebasicWordListDesc);
+
diff --git a/src/LexBullant.cpp b/src/LexBullant.cpp
new file mode 100755
index 0000000..902f89c
--- /dev/null
+++ b/src/LexBullant.cpp
@@ -0,0 +1,225 @@
+// SciTE - Scintilla based Text Editor
+// LexBullant.cxx - lexer for Bullant
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+
+static int classifyWordBullant(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler) {
+ char s[100];
+ for (unsigned int i = 0; i < end - start + 1 && i < 30; i++) {
+ s[i] = static_cast<char>(tolower(styler[start + i]));
+ s[i + 1] = '\0';
+ }
+ int lev= 0;
+ char chAttr = SCE_C_IDENTIFIER;
+ if (isdigit(s[0]) || (s[0] == '.')){
+ chAttr = SCE_C_NUMBER;
+ }
+ else {
+ if (keywords.InList(s)) {
+ chAttr = SCE_C_WORD;
+ if (strcmp(s, "end") == 0)
+ lev = -1;
+ else if (strcmp(s, "method") == 0 ||
+ strcmp(s, "case") == 0 ||
+ strcmp(s, "class") == 0 ||
+ strcmp(s, "debug") == 0 ||
+ strcmp(s, "test") == 0 ||
+ strcmp(s, "if") == 0 ||
+ strcmp(s, "lock") == 0 ||
+ strcmp(s, "transaction") == 0 ||
+ strcmp(s, "trap") == 0 ||
+ strcmp(s, "until") == 0 ||
+ strcmp(s, "while") == 0)
+ lev = 1;
+ }
+ }
+ styler.ColourTo(end, chAttr);
+ return lev;
+}
+
+static void ColouriseBullantDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+ Accessor &styler) {
+ WordList &keywords = *keywordlists[0];
+
+ styler.StartAt(startPos);
+
+ bool fold = styler.GetPropertyInt("fold") != 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
+ int levelCurrent = levelPrev;
+
+ int state = initStyle;
+ if (state == SCE_C_STRINGEOL) // Does not leak onto next line
+ state = SCE_C_DEFAULT;
+ char chPrev = ' ';
+ char chNext = styler[startPos];
+ unsigned int lengthDoc = startPos + length;
+ int visibleChars = 0;
+ styler.StartSegment(startPos);
+ int endFoundThisLine = 0;
+ for (unsigned int i = startPos; i < lengthDoc; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+
+ if ((ch == '\r' && chNext != '\n') || (ch == '\n')) {
+ // Trigger on CR only (Mac style) or either on LF from CR+LF (Dos/Win) or on LF alone (Unix)
+ // Avoid triggering two times on Dos/Win
+ // End of line
+ endFoundThisLine = 0;
+ if (state == SCE_C_STRINGEOL) {
+ styler.ColourTo(i, state);
+ state = SCE_C_DEFAULT;
+ }
+ if (fold) {
+ int lev = levelPrev;
+ if (visibleChars == 0)
+ lev |= SC_FOLDLEVELWHITEFLAG;
+ if ((levelCurrent > levelPrev) && (visibleChars > 0))
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ styler.SetLevel(lineCurrent, lev);
+ lineCurrent++;
+ levelPrev = levelCurrent;
+ }
+ visibleChars = 0;
+
+/* int indentBlock = GetLineIndentation(lineCurrent);
+ if (blockChange==1){
+ lineCurrent++;
+ int pos=SetLineIndentation(lineCurrent, indentBlock + indentSize);
+ } else if (blockChange==-1) {
+ indentBlock -= indentSize;
+ if (indentBlock < 0)
+ indentBlock = 0;
+ SetLineIndentation(lineCurrent, indentBlock);
+ lineCurrent++;
+ }
+ blockChange=0;
+*/ }
+ if (!isspace(ch))
+ visibleChars++;
+
+ if (styler.IsLeadByte(ch)) {
+ chNext = styler.SafeGetCharAt(i + 2);
+ chPrev = ' ';
+ i += 1;
+ continue;
+ }
+
+ if (state == SCE_C_DEFAULT) {
+ if (iswordstart(ch)) {
+ styler.ColourTo(i-1, state);
+ state = SCE_C_IDENTIFIER;
+ } else if (ch == '@' && chNext == 'o') {
+ if ((styler.SafeGetCharAt(i+2) =='f') && (styler.SafeGetCharAt(i+3) == 'f')) {
+ styler.ColourTo(i-1, state);
+ state = SCE_C_COMMENT;
+ }
+ } else if (ch == '#') {
+ styler.ColourTo(i-1, state);
+ state = SCE_C_COMMENTLINE;
+ } else if (ch == '\"') {
+ styler.ColourTo(i-1, state);
+ state = SCE_C_STRING;
+ } else if (ch == '\'') {
+ styler.ColourTo(i-1, state);
+ state = SCE_C_CHARACTER;
+ } else if (isoperator(ch)) {
+ styler.ColourTo(i-1, state);
+ styler.ColourTo(i, SCE_C_OPERATOR);
+ }
+ } else if (state == SCE_C_IDENTIFIER) {
+ if (!iswordchar(ch)) {
+ int levelChange = classifyWordBullant(styler.GetStartSegment(), i - 1, keywords, styler);
+ state = SCE_C_DEFAULT;
+ chNext = styler.SafeGetCharAt(i + 1);
+ if (ch == '#') {
+ state = SCE_C_COMMENTLINE;
+ } else if (ch == '\"') {
+ state = SCE_C_STRING;
+ } else if (ch == '\'') {
+ state = SCE_C_CHARACTER;
+ } else if (isoperator(ch)) {
+ styler.ColourTo(i, SCE_C_OPERATOR);
+ }
+ if (endFoundThisLine == 0)
+ levelCurrent+=levelChange;
+ if (levelChange == -1)
+ endFoundThisLine=1;
+ }
+ } else if (state == SCE_C_COMMENT) {
+ if (ch == '@' && chNext == 'o') {
+ if (styler.SafeGetCharAt(i+2) == 'n') {
+ styler.ColourTo(i+2, state);
+ state = SCE_C_DEFAULT;
+ i+=2;
+ }
+ }
+ } else if (state == SCE_C_COMMENTLINE) {
+ if (ch == '\r' || ch == '\n') {
+ endFoundThisLine = 0;
+ styler.ColourTo(i-1, state);
+ state = SCE_C_DEFAULT;
+ }
+ } else if (state == SCE_C_STRING) {
+ if (ch == '\\') {
+ if (chNext == '\"' || chNext == '\'' || chNext == '\\') {
+ i++;
+ ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ } else if (ch == '\"') {
+ styler.ColourTo(i, state);
+ state = SCE_C_DEFAULT;
+ } else if (chNext == '\r' || chNext == '\n') {
+ endFoundThisLine = 0;
+ styler.ColourTo(i-1, SCE_C_STRINGEOL);
+ state = SCE_C_STRINGEOL;
+ }
+ } else if (state == SCE_C_CHARACTER) {
+ if ((ch == '\r' || ch == '\n') && (chPrev != '\\')) {
+ endFoundThisLine = 0;
+ styler.ColourTo(i-1, SCE_C_STRINGEOL);
+ state = SCE_C_STRINGEOL;
+ } else if (ch == '\\') {
+ if (chNext == '\"' || chNext == '\'' || chNext == '\\') {
+ i++;
+ ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ } else if (ch == '\'') {
+ styler.ColourTo(i, state);
+ state = SCE_C_DEFAULT;
+ }
+ }
+ chPrev = ch;
+ }
+ styler.ColourTo(lengthDoc - 1, state);
+
+ // Fill in the real level of the next line, keeping the current flags as they will be filled in later
+ if (fold) {
+ int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
+ //styler.SetLevel(lineCurrent, levelCurrent | flagsNext);
+ styler.SetLevel(lineCurrent, levelPrev | flagsNext);
+
+ }
+}
+
+static const char * const bullantWordListDesc[] = {
+ "Keywords",
+ 0
+};
+
+LexerModule lmBullant(SCLEX_BULLANT, ColouriseBullantDoc, "bullant", 0, bullantWordListDesc);
diff --git a/src/LexCLW.cpp b/src/LexCLW.cpp
new file mode 100755
index 0000000..e28e4b1
--- /dev/null
+++ b/src/LexCLW.cpp
@@ -0,0 +1,675 @@
+// Scintilla source code edit control
+/** @file LexClw.cxx
+ ** Lexer for Clarion.
+ ** 2004/12/17 Updated Lexer
+ **/
+// Copyright 2003-2004 by Ron Schofield <ron@schofieldcomputer.com>
+// 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 <ctype.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+// Is an end of line character
+inline bool IsEOL(const int ch) {
+
+ return(ch == '\n');
+}
+
+// Convert character to uppercase
+static char CharacterUpper(char chChar) {
+
+ if (chChar < 'a' || chChar > 'z') {
+ return(chChar);
+ }
+ else {
+ return(static_cast<char>(chChar - 'a' + 'A'));
+ }
+}
+
+// Convert string to uppercase
+static void StringUpper(char *szString) {
+
+ while (*szString) {
+ *szString = CharacterUpper(*szString);
+ szString++;
+ }
+}
+
+// Is a label start character
+inline bool IsALabelStart(const int iChar) {
+
+ return(isalpha(iChar) || iChar == '_');
+}
+
+// Is a label character
+inline bool IsALabelCharacter(const int iChar) {
+
+ return(isalnum(iChar) || iChar == '_' || iChar == ':');
+}
+
+// Is the character is a ! and the the next character is not a !
+inline bool IsACommentStart(const int iChar) {
+
+ return(iChar == '!');
+}
+
+// Is the character a Clarion hex character (ABCDEF)
+inline bool IsAHexCharacter(const int iChar, bool bCaseSensitive) {
+
+ // Case insensitive.
+ if (!bCaseSensitive) {
+ if (strchr("ABCDEFabcdef", iChar) != NULL) {
+ return(true);
+ }
+ }
+ // Case sensitive
+ else {
+ if (strchr("ABCDEF", iChar) != NULL) {
+ return(true);
+ }
+ }
+ return(false);
+}
+
+// Is the character a Clarion base character (B=Binary, O=Octal, H=Hex)
+inline bool IsANumericBaseCharacter(const int iChar, bool bCaseSensitive) {
+
+ // Case insensitive.
+ if (!bCaseSensitive) {
+ // If character is a numeric base character
+ if (strchr("BOHboh", iChar) != NULL) {
+ return(true);
+ }
+ }
+ // Case sensitive
+ else {
+ // If character is a numeric base character
+ if (strchr("BOH", iChar) != NULL) {
+ return(true);
+ }
+ }
+ return(false);
+}
+
+// Set the correct numeric constant state
+inline bool SetNumericConstantState(StyleContext &scDoc) {
+
+ int iPoints = 0; // Point counter
+ char cNumericString[512]; // Numeric string buffer
+
+ // Buffer the current numberic string
+ scDoc.GetCurrent(cNumericString, sizeof(cNumericString));
+ // Loop through the string until end of string (NULL termination)
+ for (int iIndex = 0; cNumericString[iIndex] != '\0'; iIndex++) {
+ // Depending on the character
+ switch (cNumericString[iIndex]) {
+ // Is a . (point)
+ case '.' :
+ // Increment point counter
+ iPoints++;
+ break;
+ default :
+ break;
+ }
+ }
+ // If points found (can be more than one for improper formatted number
+ if (iPoints > 0) {
+ return(true);
+ }
+ // Else no points found
+ else {
+ return(false);
+ }
+}
+
+// Get the next word in uppercase from the current position (keyword lookahead)
+inline bool GetNextWordUpper(Accessor &styler, unsigned int uiStartPos, int iLength, char *cWord) {
+
+ unsigned int iIndex = 0; // Buffer Index
+
+ // Loop through the remaining string from the current position
+ for (int iOffset = uiStartPos; iOffset < iLength; iOffset++) {
+ // Get the character from the buffer using the offset
+ char cCharacter = styler[iOffset];
+ if (IsEOL(cCharacter)) {
+ break;
+ }
+ // If the character is alphabet character
+ if (isalpha(cCharacter)) {
+ // Add UPPERCASE character to the word buffer
+ cWord[iIndex++] = CharacterUpper(cCharacter);
+ }
+ }
+ // Add null termination
+ cWord[iIndex] = '\0';
+ // If no word was found
+ if (iIndex == 0) {
+ // Return failure
+ return(false);
+ }
+ // Else word was found
+ else {
+ // Return success
+ return(true);
+ }
+}
+
+// Clarion Language Colouring Procedure
+static void ColouriseClarionDoc(unsigned int uiStartPos, int iLength, int iInitStyle, WordList *wlKeywords[], Accessor &accStyler, bool bCaseSensitive) {
+
+ int iParenthesesLevel = 0; // Parenthese Level
+ int iColumn1Label = false; // Label starts in Column 1
+
+ WordList &wlClarionKeywords = *wlKeywords[0]; // Clarion Keywords
+ WordList &wlCompilerDirectives = *wlKeywords[1]; // Compiler Directives
+ WordList &wlRuntimeExpressions = *wlKeywords[2]; // Runtime Expressions
+ WordList &wlBuiltInProcsFuncs = *wlKeywords[3]; // Builtin Procedures and Functions
+ WordList &wlStructsDataTypes = *wlKeywords[4]; // Structures and Data Types
+ WordList &wlAttributes = *wlKeywords[5]; // Procedure Attributes
+ WordList &wlStandardEquates = *wlKeywords[6]; // Standard Equates
+ WordList &wlLabelReservedWords = *wlKeywords[7]; // Clarion Reserved Keywords (Labels)
+ WordList &wlProcLabelReservedWords = *wlKeywords[8]; // Clarion Reserved Keywords (Procedure Labels)
+
+ const char wlProcReservedKeywordList[] =
+ "PROCEDURE FUNCTION";
+ WordList wlProcReservedKeywords;
+ wlProcReservedKeywords.Set(wlProcReservedKeywordList);
+
+ const char wlCompilerKeywordList[] =
+ "COMPILE OMIT";
+ WordList wlCompilerKeywords;
+ wlCompilerKeywords.Set(wlCompilerKeywordList);
+
+ const char wlLegacyStatementsList[] =
+ "BOF EOF FUNCTION POINTER SHARE";
+ WordList wlLegacyStatements;
+ wlLegacyStatements.Set(wlLegacyStatementsList);
+
+ StyleContext scDoc(uiStartPos, iLength, iInitStyle, accStyler);
+
+ // lex source code
+ for (; scDoc.More(); scDoc.Forward())
+ {
+ //
+ // Determine if the current state should terminate.
+ //
+
+ // Label State Handling
+ if (scDoc.state == SCE_CLW_LABEL) {
+ // If the character is not a valid label
+ if (!IsALabelCharacter(scDoc.ch)) {
+ // If the character is a . (dot syntax)
+ if (scDoc.ch == '.') {
+ // Turn off column 1 label flag as label now cannot be reserved work
+ iColumn1Label = false;
+ // Uncolour the . (dot) to default state, move forward one character,
+ // and change back to the label state.
+ scDoc.SetState(SCE_CLW_DEFAULT);
+ scDoc.Forward();
+ scDoc.SetState(SCE_CLW_LABEL);
+ }
+ // Else check label
+ else {
+ char cLabel[512]; // Label buffer
+ // Buffer the current label string
+ scDoc.GetCurrent(cLabel,sizeof(cLabel));
+ // If case insensitive, convert string to UPPERCASE to match passed keywords.
+ if (!bCaseSensitive) {
+ StringUpper(cLabel);
+ }
+ // Else if UPPERCASE label string is in the Clarion compiler keyword list
+ if (wlCompilerKeywords.InList(cLabel) && iColumn1Label){
+ // change the label to error state
+ scDoc.ChangeState(SCE_CLW_COMPILER_DIRECTIVE);
+ }
+ // Else if UPPERCASE label string is in the Clarion reserved keyword list
+ else if (wlLabelReservedWords.InList(cLabel) && iColumn1Label){
+ // change the label to error state
+ scDoc.ChangeState(SCE_CLW_ERROR);
+ }
+ // Else if UPPERCASE label string is
+ else if (wlProcLabelReservedWords.InList(cLabel) && iColumn1Label) {
+ char cWord[512]; // Word buffer
+ // Get the next word from the current position
+ if (GetNextWordUpper(accStyler,scDoc.currentPos,uiStartPos+iLength,cWord)) {
+ // If the next word is a procedure reserved word
+ if (wlProcReservedKeywords.InList(cWord)) {
+ // Change the label to error state
+ scDoc.ChangeState(SCE_CLW_ERROR);
+ }
+ }
+ }
+ // Else if label string is in the compiler directive keyword list
+ else if (wlCompilerDirectives.InList(cLabel)) {
+ // change the state to compiler directive state
+ scDoc.ChangeState(SCE_CLW_COMPILER_DIRECTIVE);
+ }
+ // Terminate the label state and set to default state
+ scDoc.SetState(SCE_CLW_DEFAULT);
+ }
+ }
+ }
+ // Keyword State Handling
+ else if (scDoc.state == SCE_CLW_KEYWORD) {
+ // If character is : (colon)
+ if (scDoc.ch == ':') {
+ char cEquate[512]; // Equate buffer
+ // Move forward to include : (colon) in buffer
+ scDoc.Forward();
+ // Buffer the equate string
+ scDoc.GetCurrent(cEquate,sizeof(cEquate));
+ // If case insensitive, convert string to UPPERCASE to match passed keywords.
+ if (!bCaseSensitive) {
+ StringUpper(cEquate);
+ }
+ // If statement string is in the equate list
+ if (wlStandardEquates.InList(cEquate)) {
+ // Change to equate state
+ scDoc.ChangeState(SCE_CLW_STANDARD_EQUATE);
+ }
+ }
+ // If the character is not a valid label character
+ else if (!IsALabelCharacter(scDoc.ch)) {
+ char cStatement[512]; // Statement buffer
+ // Buffer the statement string
+ scDoc.GetCurrent(cStatement,sizeof(cStatement));
+ // If case insensitive, convert string to UPPERCASE to match passed keywords.
+ if (!bCaseSensitive) {
+ StringUpper(cStatement);
+ }
+ // If statement string is in the Clarion keyword list
+ if (wlClarionKeywords.InList(cStatement)) {
+ // Change the statement string to the Clarion keyword state
+ scDoc.ChangeState(SCE_CLW_KEYWORD);
+ }
+ // Else if statement string is in the compiler directive keyword list
+ else if (wlCompilerDirectives.InList(cStatement)) {
+ // Change the statement string to the compiler directive state
+ scDoc.ChangeState(SCE_CLW_COMPILER_DIRECTIVE);
+ }
+ // Else if statement string is in the runtime expressions keyword list
+ else if (wlRuntimeExpressions.InList(cStatement)) {
+ // Change the statement string to the runtime expressions state
+ scDoc.ChangeState(SCE_CLW_RUNTIME_EXPRESSIONS);
+ }
+ // Else if statement string is in the builtin procedures and functions keyword list
+ else if (wlBuiltInProcsFuncs.InList(cStatement)) {
+ // Change the statement string to the builtin procedures and functions state
+ scDoc.ChangeState(SCE_CLW_BUILTIN_PROCEDURES_FUNCTION);
+ }
+ // Else if statement string is in the tructures and data types keyword list
+ else if (wlStructsDataTypes.InList(cStatement)) {
+ // Change the statement string to the structures and data types state
+ scDoc.ChangeState(SCE_CLW_STRUCTURE_DATA_TYPE);
+ }
+ // Else if statement string is in the procedure attribute keyword list
+ else if (wlAttributes.InList(cStatement)) {
+ // Change the statement string to the procedure attribute state
+ scDoc.ChangeState(SCE_CLW_ATTRIBUTE);
+ }
+ // Else if statement string is in the standard equate keyword list
+ else if (wlStandardEquates.InList(cStatement)) {
+ // Change the statement string to the standard equate state
+ scDoc.ChangeState(SCE_CLW_STANDARD_EQUATE);
+ }
+ // Else if statement string is in the deprecated or legacy keyword list
+ else if (wlLegacyStatements.InList(cStatement)) {
+ // Change the statement string to the standard equate state
+ scDoc.ChangeState(SCE_CLW_DEPRECATED);
+ }
+ // Else the statement string doesn't match any work list
+ else {
+ // Change the statement string to the default state
+ scDoc.ChangeState(SCE_CLW_DEFAULT);
+ }
+ // Terminate the keyword state and set to default state
+ scDoc.SetState(SCE_CLW_DEFAULT);
+ }
+ }
+ // String State Handling
+ else if (scDoc.state == SCE_CLW_STRING) {
+ // If the character is an ' (single quote)
+ if (scDoc.ch == '\'') {
+ // Set the state to default and move forward colouring
+ // the ' (single quote) as default state
+ // terminating the string state
+ scDoc.SetState(SCE_CLW_DEFAULT);
+ scDoc.Forward();
+ }
+ // If the next character is an ' (single quote)
+ if (scDoc.chNext == '\'') {
+ // Move forward one character and set to default state
+ // colouring the next ' (single quote) as default state
+ // terminating the string state
+ scDoc.ForwardSetState(SCE_CLW_DEFAULT);
+ scDoc.Forward();
+ }
+ }
+ // Picture String State Handling
+ else if (scDoc.state == SCE_CLW_PICTURE_STRING) {
+ // If the character is an ( (open parenthese)
+ if (scDoc.ch == '(') {
+ // Increment the parenthese level
+ iParenthesesLevel++;
+ }
+ // Else if the character is a ) (close parenthese)
+ else if (scDoc.ch == ')') {
+ // If the parenthese level is set to zero
+ // parentheses matched
+ if (!iParenthesesLevel) {
+ scDoc.SetState(SCE_CLW_DEFAULT);
+ }
+ // Else parenthese level is greater than zero
+ // still looking for matching parentheses
+ else {
+ // Decrement the parenthese level
+ iParenthesesLevel--;
+ }
+ }
+ }
+ // Standard Equate State Handling
+ else if (scDoc.state == SCE_CLW_STANDARD_EQUATE) {
+ if (!isalnum(scDoc.ch)) {
+ scDoc.SetState(SCE_CLW_DEFAULT);
+ }
+ }
+ // Integer Constant State Handling
+ else if (scDoc.state == SCE_CLW_INTEGER_CONSTANT) {
+ // If the character is not a digit (0-9)
+ // or character is not a hexidecimal character (A-F)
+ // or character is not a . (point)
+ // or character is not a numberic base character (B,O,H)
+ if (!(isdigit(scDoc.ch)
+ || IsAHexCharacter(scDoc.ch, bCaseSensitive)
+ || scDoc.ch == '.'
+ || IsANumericBaseCharacter(scDoc.ch, bCaseSensitive))) {
+ // If the number was a real
+ if (SetNumericConstantState(scDoc)) {
+ // Colour the matched string to the real constant state
+ scDoc.ChangeState(SCE_CLW_REAL_CONSTANT);
+ }
+ // Else the number was an integer
+ else {
+ // Colour the matched string to an integer constant state
+ scDoc.ChangeState(SCE_CLW_INTEGER_CONSTANT);
+ }
+ // Terminate the integer constant state and set to default state
+ scDoc.SetState(SCE_CLW_DEFAULT);
+ }
+ }
+
+ //
+ // Determine if a new state should be entered.
+ //
+
+ // Beginning of Line Handling
+ if (scDoc.atLineStart) {
+ // Reset the column 1 label flag
+ iColumn1Label = false;
+ // If column 1 character is a label start character
+ if (IsALabelStart(scDoc.ch)) {
+ // Label character is found in column 1
+ // so set column 1 label flag and clear last column 1 label
+ iColumn1Label = true;
+ // Set the state to label
+ scDoc.SetState(SCE_CLW_LABEL);
+ }
+ // else if character is a space or tab
+ else if (IsASpace(scDoc.ch)){
+ // Set to default state
+ scDoc.SetState(SCE_CLW_DEFAULT);
+ }
+ // else if comment start (!) or is an * (asterisk)
+ else if (IsACommentStart(scDoc.ch) || scDoc.ch == '*' ) {
+ // then set the state to comment.
+ scDoc.SetState(SCE_CLW_COMMENT);
+ }
+ // else the character is a ? (question mark)
+ else if (scDoc.ch == '?') {
+ // Change to the compiler directive state, move forward,
+ // colouring the ? (question mark), change back to default state.
+ scDoc.ChangeState(SCE_CLW_COMPILER_DIRECTIVE);
+ scDoc.Forward();
+ scDoc.SetState(SCE_CLW_DEFAULT);
+ }
+ // else an invalid character in column 1
+ else {
+ // Set to error state
+ scDoc.SetState(SCE_CLW_ERROR);
+ }
+ }
+ // End of Line Handling
+ else if (scDoc.atLineEnd) {
+ // Reset to the default state at the end of each line.
+ scDoc.SetState(SCE_CLW_DEFAULT);
+ }
+ // Default Handling
+ else {
+ // If in default state
+ if (scDoc.state == SCE_CLW_DEFAULT) {
+ // If is a letter could be a possible statement
+ if (isalpha(scDoc.ch)) {
+ // Set the state to Clarion Keyword and verify later
+ scDoc.SetState(SCE_CLW_KEYWORD);
+ }
+ // else is a number
+ else if (isdigit(scDoc.ch)) {
+ // Set the state to Integer Constant and verify later
+ scDoc.SetState(SCE_CLW_INTEGER_CONSTANT);
+ }
+ // else if the start of a comment or a | (line continuation)
+ else if (IsACommentStart(scDoc.ch) || scDoc.ch == '|') {
+ // then set the state to comment.
+ scDoc.SetState(SCE_CLW_COMMENT);
+ }
+ // else if the character is a ' (single quote)
+ else if (scDoc.ch == '\'') {
+ // If the character is also a ' (single quote)
+ // Embedded Apostrophe
+ if (scDoc.chNext == '\'') {
+ // Move forward colouring it as default state
+ scDoc.ForwardSetState(SCE_CLW_DEFAULT);
+ }
+ else {
+ // move to the next character and then set the state to comment.
+ scDoc.ForwardSetState(SCE_CLW_STRING);
+ }
+ }
+ // else the character is an @ (ampersand)
+ else if (scDoc.ch == '@') {
+ // Case insensitive.
+ if (!bCaseSensitive) {
+ // If character is a valid picture token character
+ if (strchr("DEKNPSTdeknpst", scDoc.chNext) != NULL) {
+ // Set to the picture string state
+ scDoc.SetState(SCE_CLW_PICTURE_STRING);
+ }
+ }
+ // Case sensitive
+ else {
+ // If character is a valid picture token character
+ if (strchr("DEKNPST", scDoc.chNext) != NULL) {
+ // Set the picture string state
+ scDoc.SetState(SCE_CLW_PICTURE_STRING);
+ }
+ }
+ }
+ }
+ }
+ }
+ // lexing complete
+ scDoc.Complete();
+}
+
+// Clarion Language Case Sensitive Colouring Procedure
+static void ColouriseClarionDocSensitive(unsigned int uiStartPos, int iLength, int iInitStyle, WordList *wlKeywords[], Accessor &accStyler) {
+
+ ColouriseClarionDoc(uiStartPos, iLength, iInitStyle, wlKeywords, accStyler, true);
+}
+
+// Clarion Language Case Insensitive Colouring Procedure
+static void ColouriseClarionDocInsensitive(unsigned int uiStartPos, int iLength, int iInitStyle, WordList *wlKeywords[], Accessor &accStyler) {
+
+ ColouriseClarionDoc(uiStartPos, iLength, iInitStyle, wlKeywords, accStyler, false);
+}
+
+// Fill Buffer
+
+static void FillBuffer(unsigned int uiStart, unsigned int uiEnd, Accessor &accStyler, char *szBuffer, unsigned int uiLength) {
+
+ unsigned int uiPos = 0;
+
+ while ((uiPos < uiEnd - uiStart + 1) && (uiPos < uiLength-1)) {
+ szBuffer[uiPos] = static_cast<char>(toupper(accStyler[uiStart + uiPos]));
+ uiPos++;
+ }
+ szBuffer[uiPos] = '\0';
+}
+
+// Classify Clarion Fold Point
+
+static int ClassifyClarionFoldPoint(int iLevel, const char* szString) {
+
+ if (!(isdigit(szString[0]) || (szString[0] == '.'))) {
+ if (strcmp(szString, "PROCEDURE") == 0) {
+ // iLevel = SC_FOLDLEVELBASE + 1;
+ }
+ else if (strcmp(szString, "MAP") == 0 ||
+ strcmp(szString,"ACCEPT") == 0 ||
+ strcmp(szString,"BEGIN") == 0 ||
+ strcmp(szString,"CASE") == 0 ||
+ strcmp(szString,"EXECUTE") == 0 ||
+ strcmp(szString,"IF") == 0 ||
+ strcmp(szString,"ITEMIZE") == 0 ||
+ strcmp(szString,"INTERFACE") == 0 ||
+ strcmp(szString,"JOIN") == 0 ||
+ strcmp(szString,"LOOP") == 0 ||
+ strcmp(szString,"MODULE") == 0 ||
+ strcmp(szString,"RECORD") == 0) {
+ iLevel++;
+ }
+ else if (strcmp(szString, "APPLICATION") == 0 ||
+ strcmp(szString, "CLASS") == 0 ||
+ strcmp(szString, "DETAIL") == 0 ||
+ strcmp(szString, "FILE") == 0 ||
+ strcmp(szString, "FOOTER") == 0 ||
+ strcmp(szString, "FORM") == 0 ||
+ strcmp(szString, "GROUP") == 0 ||
+ strcmp(szString, "HEADER") == 0 ||
+ strcmp(szString, "INTERFACE") == 0 ||
+ strcmp(szString, "MENU") == 0 ||
+ strcmp(szString, "MENUBAR") == 0 ||
+ strcmp(szString, "OLE") == 0 ||
+ strcmp(szString, "OPTION") == 0 ||
+ strcmp(szString, "QUEUE") == 0 ||
+ strcmp(szString, "REPORT") == 0 ||
+ strcmp(szString, "SHEET") == 0 ||
+ strcmp(szString, "TAB") == 0 ||
+ strcmp(szString, "TOOLBAR") == 0 ||
+ strcmp(szString, "VIEW") == 0 ||
+ strcmp(szString, "WINDOW") == 0) {
+ iLevel++;
+ }
+ else if (strcmp(szString, "END") == 0 ||
+ strcmp(szString, "UNTIL") == 0 ||
+ strcmp(szString, "WHILE") == 0) {
+ iLevel--;
+ }
+ }
+ return(iLevel);
+}
+
+// Clarion Language Folding Procedure
+static void FoldClarionDoc(unsigned int uiStartPos, int iLength, int iInitStyle, WordList *[], Accessor &accStyler) {
+
+ unsigned int uiEndPos = uiStartPos + iLength;
+ int iLineCurrent = accStyler.GetLine(uiStartPos);
+ int iLevelPrev = accStyler.LevelAt(iLineCurrent) & SC_FOLDLEVELNUMBERMASK;
+ int iLevelCurrent = iLevelPrev;
+ char chNext = accStyler[uiStartPos];
+ int iStyle = iInitStyle;
+ int iStyleNext = accStyler.StyleAt(uiStartPos);
+ int iVisibleChars = 0;
+ int iLastStart = 0;
+
+ for (unsigned int uiPos = uiStartPos; uiPos < uiEndPos; uiPos++) {
+
+ char chChar = chNext;
+ chNext = accStyler.SafeGetCharAt(uiPos + 1);
+ int iStylePrev = iStyle;
+ iStyle = iStyleNext;
+ iStyleNext = accStyler.StyleAt(uiPos + 1);
+ bool bEOL = (chChar == '\r' && chNext != '\n') || (chChar == '\n');
+
+ if (iStylePrev == SCE_CLW_DEFAULT) {
+ if (iStyle == SCE_CLW_KEYWORD || iStyle == SCE_CLW_STRUCTURE_DATA_TYPE) {
+ // Store last word start point.
+ iLastStart = uiPos;
+ }
+ }
+
+ if (iStylePrev == SCE_CLW_KEYWORD || iStylePrev == SCE_CLW_STRUCTURE_DATA_TYPE) {
+ if(iswordchar(chChar) && !iswordchar(chNext)) {
+ char chBuffer[100];
+ FillBuffer(iLastStart, uiPos, accStyler, chBuffer, sizeof(chBuffer));
+ iLevelCurrent = ClassifyClarionFoldPoint(iLevelCurrent,chBuffer);
+ // if ((iLevelCurrent == SC_FOLDLEVELBASE + 1) && iLineCurrent > 1) {
+ // accStyler.SetLevel(iLineCurrent-1,SC_FOLDLEVELBASE);
+ // iLevelPrev = SC_FOLDLEVELBASE;
+ // }
+ }
+ }
+
+ if (bEOL) {
+ int iLevel = iLevelPrev;
+ if ((iLevelCurrent > iLevelPrev) && (iVisibleChars > 0))
+ iLevel |= SC_FOLDLEVELHEADERFLAG;
+ if (iLevel != accStyler.LevelAt(iLineCurrent)) {
+ accStyler.SetLevel(iLineCurrent,iLevel);
+ }
+ iLineCurrent++;
+ iLevelPrev = iLevelCurrent;
+ iVisibleChars = 0;
+ }
+
+ if (!isspacechar(chChar))
+ iVisibleChars++;
+ }
+
+ // Fill in the real level of the next line, keeping the current flags
+ // as they will be filled in later.
+ int iFlagsNext = accStyler.LevelAt(iLineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
+ accStyler.SetLevel(iLineCurrent, iLevelPrev | iFlagsNext);
+}
+
+// Word List Descriptions
+static const char * const rgWordListDescriptions[] = {
+ "Clarion Keywords",
+ "Compiler Directives",
+ "Built-in Procedures and Functions",
+ "Runtime Expressions",
+ "Structure and Data Types",
+ "Attributes",
+ "Standard Equates",
+ "Reserved Words (Labels)",
+ "Reserved Words (Procedure Labels)",
+ 0,
+};
+
+// Case Sensitive Clarion Language Lexer
+LexerModule lmClw(SCLEX_CLW, ColouriseClarionDocSensitive, "clarion", FoldClarionDoc, rgWordListDescriptions);
+
+// Case Insensitive Clarion Language Lexer
+LexerModule lmClwNoCase(SCLEX_CLWNOCASE, ColouriseClarionDocInsensitive, "clarionnocase", FoldClarionDoc, rgWordListDescriptions);
diff --git a/src/LexCPP.cpp b/src/LexCPP.cpp
new file mode 100755
index 0000000..1db0c7c
--- /dev/null
+++ b/src/LexCPP.cpp
@@ -0,0 +1,489 @@
+// Scintilla source code edit control
+/** @file LexCPP.cxx
+ ** Lexer for C++, C, Java, and JavaScript.
+ **/
+// Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+#define SET_LOWER "abcdefghijklmnopqrstuvwxyz"
+#define SET_UPPER "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define SET_DIGITS "0123456789"
+
+class SetOfCharacters {
+ int size;
+ bool valueAfter;
+ bool *bset;
+public:
+ SetOfCharacters(const char *setOfCharacters, int size_=0x80, bool valueAfter_=false) {
+ size = size_;
+ valueAfter = valueAfter_;
+ bset = new bool[size];
+ for (int i=0; i < size; i++) {
+ bset[i] = false;
+ }
+ for (const char *cp=setOfCharacters; *cp; cp++) {
+ int val = static_cast<unsigned char>(*cp);
+ PLATFORM_ASSERT(val >= 0);
+ PLATFORM_ASSERT(val < size);
+ bset[val] = true;
+ }
+ }
+ ~SetOfCharacters() {
+ delete []bset;
+ bset = 0;
+ size = 0;
+ }
+ void Add(int val) {
+ PLATFORM_ASSERT(val >= 0);
+ PLATFORM_ASSERT(val < size);
+ bset[val] = true;
+ }
+ bool Contains(int val) {
+ PLATFORM_ASSERT(val >= 0);
+ return (val < size) ? bset[val] : valueAfter;
+ }
+};
+
+static bool IsSpaceEquiv(int state) {
+ return (state <= SCE_C_COMMENTDOC) ||
+ // including SCE_C_DEFAULT, SCE_C_COMMENT, SCE_C_COMMENTLINE
+ (state == SCE_C_COMMENTLINEDOC) || (state == SCE_C_COMMENTDOCKEYWORD) ||
+ (state == SCE_C_COMMENTDOCKEYWORDERROR);
+}
+
+static void ColouriseCppDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+ Accessor &styler, bool caseSensitive) {
+
+ WordList &keywords = *keywordlists[0];
+ WordList &keywords2 = *keywordlists[1];
+ WordList &keywords3 = *keywordlists[2];
+ WordList &keywords4 = *keywordlists[3];
+
+ bool stylingWithinPreprocessor = styler.GetPropertyInt("styling.within.preprocessor") != 0;
+
+ SetOfCharacters setOKBeforeRE("(=,");
+
+ SetOfCharacters setDoxygen("$@\\&<>#{}[]" SET_LOWER);
+
+ SetOfCharacters setWordStart("_" SET_LOWER SET_UPPER, 0x80, true);
+ SetOfCharacters setWord("._" SET_LOWER SET_UPPER SET_DIGITS, 0x80, true);
+ if (styler.GetPropertyInt("lexer.cpp.allow.dollars", 1) != 0) {
+ setWordStart.Add('$');
+ setWord.Add('$');
+ }
+
+ int chPrevNonWhite = ' ';
+ int visibleChars = 0;
+ bool lastWordWasUUID = false;
+ int styleBeforeDCKeyword = SCE_C_DEFAULT;
+ bool continuationLine = false;
+
+ if (initStyle == SCE_C_PREPROCESSOR) {
+ // Set continuationLine if last character of previous line is '\'
+ int lineCurrent = styler.GetLine(startPos);
+ if (lineCurrent > 0) {
+ int chBack = styler.SafeGetCharAt(startPos-1, 0);
+ int chBack2 = styler.SafeGetCharAt(startPos-2, 0);
+ int lineEndChar = '!';
+ if (chBack2 == '\r' && chBack == '\n') {
+ lineEndChar = styler.SafeGetCharAt(startPos-3, 0);
+ } else if (chBack == '\n' || chBack == '\r') {
+ lineEndChar = chBack2;
+ }
+ continuationLine = lineEndChar == '\\';
+ }
+ }
+
+ // look back to set chPrevNonWhite properly for better regex colouring
+ if (startPos > 0) {
+ int back = startPos;
+ while (--back && IsSpaceEquiv(styler.StyleAt(back)))
+ ;
+ if (styler.StyleAt(back) == SCE_C_OPERATOR) {
+ chPrevNonWhite = styler.SafeGetCharAt(back);
+ }
+ }
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ for (; sc.More(); sc.Forward()) {
+
+ if (sc.atLineStart) {
+ if (sc.state == SCE_C_STRING) {
+ // Prevent SCE_C_STRINGEOL from leaking back to previous line which
+ // ends with a line continuation by locking in the state upto this position.
+ sc.SetState(SCE_C_STRING);
+ }
+ // Reset states to begining of colourise so no surprises
+ // if different sets of lines lexed.
+ visibleChars = 0;
+ lastWordWasUUID = false;
+ }
+
+ // Handle line continuation generically.
+ if (sc.ch == '\\') {
+ if (sc.chNext == '\n' || sc.chNext == '\r') {
+ sc.Forward();
+ if (sc.ch == '\r' && sc.chNext == '\n') {
+ sc.Forward();
+ }
+ continuationLine = true;
+ continue;
+ }
+ }
+
+ // Determine if the current state should terminate.
+ switch (sc.state) {
+ case SCE_C_OPERATOR:
+ sc.SetState(SCE_C_DEFAULT);
+ break;
+ case SCE_C_NUMBER:
+ // We accept almost anything because of hex. and number suffixes
+ if (!setWord.Contains(sc.ch)) {
+ sc.SetState(SCE_C_DEFAULT);
+ }
+ break;
+ case SCE_C_IDENTIFIER:
+ if (!setWord.Contains(sc.ch) || (sc.ch == '.')) {
+ char s[1000];
+ if (caseSensitive) {
+ sc.GetCurrent(s, sizeof(s));
+ } else {
+ sc.GetCurrentLowered(s, sizeof(s));
+ }
+ if (keywords.InList(s)) {
+ lastWordWasUUID = strcmp(s, "uuid") == 0;
+ sc.ChangeState(SCE_C_WORD);
+ } else if (keywords2.InList(s)) {
+ sc.ChangeState(SCE_C_WORD2);
+ } else if (keywords4.InList(s)) {
+ sc.ChangeState(SCE_C_GLOBALCLASS);
+ }
+ sc.SetState(SCE_C_DEFAULT);
+ }
+ break;
+ case SCE_C_PREPROCESSOR:
+ if (sc.atLineStart && !continuationLine) {
+ sc.SetState(SCE_C_DEFAULT);
+ } else if (stylingWithinPreprocessor) {
+ if (IsASpace(sc.ch)) {
+ sc.SetState(SCE_C_DEFAULT);
+ }
+ } else {
+ if (sc.Match('/', '*') || sc.Match('/', '/')) {
+ sc.SetState(SCE_C_DEFAULT);
+ }
+ }
+ break;
+ case SCE_C_COMMENT:
+ if (sc.Match('*', '/')) {
+ sc.Forward();
+ sc.ForwardSetState(SCE_C_DEFAULT);
+ }
+ break;
+ case SCE_C_COMMENTDOC:
+ if (sc.Match('*', '/')) {
+ sc.Forward();
+ sc.ForwardSetState(SCE_C_DEFAULT);
+ } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support
+ // Verify that we have the conditions to mark a comment-doc-keyword
+ if ((IsASpace(sc.chPrev) || sc.chPrev == '*') && (!IsASpace(sc.chNext))) {
+ styleBeforeDCKeyword = SCE_C_COMMENTDOC;
+ sc.SetState(SCE_C_COMMENTDOCKEYWORD);
+ }
+ }
+ break;
+ case SCE_C_COMMENTLINE:
+ if (sc.atLineStart) {
+ sc.SetState(SCE_C_DEFAULT);
+ }
+ break;
+ case SCE_C_COMMENTLINEDOC:
+ if (sc.atLineStart) {
+ sc.SetState(SCE_C_DEFAULT);
+ } else if (sc.ch == '@' || sc.ch == '\\') { // JavaDoc and Doxygen support
+ // Verify that we have the conditions to mark a comment-doc-keyword
+ if ((IsASpace(sc.chPrev) || sc.chPrev == '/' || sc.chPrev == '!') && (!IsASpace(sc.chNext))) {
+ styleBeforeDCKeyword = SCE_C_COMMENTLINEDOC;
+ sc.SetState(SCE_C_COMMENTDOCKEYWORD);
+ }
+ }
+ break;
+ case SCE_C_COMMENTDOCKEYWORD:
+ if ((styleBeforeDCKeyword == SCE_C_COMMENTDOC) && sc.Match('*', '/')) {
+ sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR);
+ sc.Forward();
+ sc.ForwardSetState(SCE_C_DEFAULT);
+ } else if (!setDoxygen.Contains(sc.ch)) {
+ char s[100];
+ if (caseSensitive) {
+ sc.GetCurrent(s, sizeof(s));
+ } else {
+ sc.GetCurrentLowered(s, sizeof(s));
+ }
+ if (!IsASpace(sc.ch) || !keywords3.InList(s + 1)) {
+ sc.ChangeState(SCE_C_COMMENTDOCKEYWORDERROR);
+ }
+ sc.SetState(styleBeforeDCKeyword);
+ }
+ break;
+ case SCE_C_STRING:
+ if (sc.atLineEnd) {
+ sc.ChangeState(SCE_C_STRINGEOL);
+ } else if (sc.ch == '\\') {
+ if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
+ sc.Forward();
+ }
+ } else if (sc.ch == '\"') {
+ sc.ForwardSetState(SCE_C_DEFAULT);
+ }
+ break;
+ case SCE_C_CHARACTER:
+ if (sc.atLineEnd) {
+ sc.ChangeState(SCE_C_STRINGEOL);
+ } else if (sc.ch == '\\') {
+ if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
+ sc.Forward();
+ }
+ } else if (sc.ch == '\'') {
+ sc.ForwardSetState(SCE_C_DEFAULT);
+ }
+ break;
+ case SCE_C_REGEX:
+ if (sc.atLineStart) {
+ sc.SetState(SCE_C_DEFAULT);
+ } else if (sc.ch == '/') {
+ sc.Forward();
+ while ((sc.ch < 0x80) && islower(sc.ch))
+ sc.Forward(); // gobble regex flags
+ sc.SetState(SCE_C_DEFAULT);
+ } else if (sc.ch == '\\') {
+ // Gobble up the quoted character
+ if (sc.chNext == '\\' || sc.chNext == '/') {
+ sc.Forward();
+ }
+ }
+ break;
+ case SCE_C_STRINGEOL:
+ if (sc.atLineStart) {
+ sc.SetState(SCE_C_DEFAULT);
+ }
+ break;
+ case SCE_C_VERBATIM:
+ if (sc.ch == '\"') {
+ if (sc.chNext == '\"') {
+ sc.Forward();
+ } else {
+ sc.ForwardSetState(SCE_C_DEFAULT);
+ }
+ }
+ break;
+ case SCE_C_UUID:
+ if (sc.ch == '\r' || sc.ch == '\n' || sc.ch == ')') {
+ sc.SetState(SCE_C_DEFAULT);
+ }
+ }
+
+ // Determine if a new state should be entered.
+ if (sc.state == SCE_C_DEFAULT) {
+ if (sc.Match('@', '\"')) {
+ sc.SetState(SCE_C_VERBATIM);
+ sc.Forward();
+ } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
+ if (lastWordWasUUID) {
+ sc.SetState(SCE_C_UUID);
+ lastWordWasUUID = false;
+ } else {
+ sc.SetState(SCE_C_NUMBER);
+ }
+ } else if (setWordStart.Contains(sc.ch) || (sc.ch == '@')) {
+ if (lastWordWasUUID) {
+ sc.SetState(SCE_C_UUID);
+ lastWordWasUUID = false;
+ } else {
+ sc.SetState(SCE_C_IDENTIFIER);
+ }
+ } else if (sc.Match('/', '*')) {
+ if (sc.Match("/**") || sc.Match("/*!")) { // Support of Qt/Doxygen doc. style
+ sc.SetState(SCE_C_COMMENTDOC);
+ } else {
+ sc.SetState(SCE_C_COMMENT);
+ }
+ sc.Forward(); // Eat the * so it isn't used for the end of the comment
+ } else if (sc.Match('/', '/')) {
+ if ((sc.Match("///") && !sc.Match("////")) || sc.Match("//!"))
+ // Support of Qt/Doxygen doc. style
+ sc.SetState(SCE_C_COMMENTLINEDOC);
+ else
+ sc.SetState(SCE_C_COMMENTLINE);
+ } else if (sc.ch == '/' && setOKBeforeRE.Contains(chPrevNonWhite)) {
+ sc.SetState(SCE_C_REGEX); // JavaScript's RegEx
+ } else if (sc.ch == '\"') {
+ sc.SetState(SCE_C_STRING);
+ } else if (sc.ch == '\'') {
+ sc.SetState(SCE_C_CHARACTER);
+ } else if (sc.ch == '#' && visibleChars == 0) {
+ // Preprocessor commands are alone on their line
+ sc.SetState(SCE_C_PREPROCESSOR);
+ // Skip whitespace between # and preprocessor word
+ do {
+ sc.Forward();
+ } while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_C_DEFAULT);
+ }
+ } else if (isoperator(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_C_OPERATOR);
+ }
+ }
+
+ if (!IsASpace(sc.ch) && !IsSpaceEquiv(sc.state)) {
+ chPrevNonWhite = sc.ch;
+ visibleChars++;
+ }
+ continuationLine = false;
+ }
+ sc.Complete();
+}
+
+static bool IsStreamCommentStyle(int style) {
+ return style == SCE_C_COMMENT ||
+ style == SCE_C_COMMENTDOC ||
+ style == SCE_C_COMMENTDOCKEYWORD ||
+ style == SCE_C_COMMENTDOCKEYWORDERROR;
+}
+
+// Store both the current line's fold level and the next lines in the
+// level store to make it easy to pick up with each increment
+// and to make it possible to fiddle the current level for "} else {".
+static void FoldNoBoxCppDoc(unsigned int startPos, int length, int initStyle,
+ Accessor &styler) {
+ bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
+ bool foldPreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0;
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0;
+ unsigned int endPos = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelCurrent = SC_FOLDLEVELBASE;
+ if (lineCurrent > 0)
+ levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
+ int levelMinCurrent = levelCurrent;
+ int levelNext = levelCurrent;
+ char chNext = styler[startPos];
+ int styleNext = styler.StyleAt(startPos);
+ int style = initStyle;
+ for (unsigned int i = startPos; i < endPos; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ int stylePrev = style;
+ style = styleNext;
+ styleNext = styler.StyleAt(i + 1);
+ bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
+ if (foldComment && IsStreamCommentStyle(style)) {
+ if (!IsStreamCommentStyle(stylePrev)) {
+ levelNext++;
+ } else if (!IsStreamCommentStyle(styleNext) && !atEOL) {
+ // Comments don't end at end of line and the next character may be unstyled.
+ levelNext--;
+ }
+ }
+ if (foldComment && (style == SCE_C_COMMENTLINE)) {
+ if ((ch == '/') && (chNext == '/')) {
+ char chNext2 = styler.SafeGetCharAt(i + 2);
+ if (chNext2 == '{') {
+ levelNext++;
+ } else if (chNext2 == '}') {
+ levelNext--;
+ }
+ }
+ }
+ if (foldPreprocessor && (style == SCE_C_PREPROCESSOR)) {
+ if (ch == '#') {
+ unsigned int j = i + 1;
+ while ((j < endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) {
+ j++;
+ }
+ if (styler.Match(j, "region") || styler.Match(j, "if")) {
+ levelNext++;
+ } else if (styler.Match(j, "end")) {
+ levelNext--;
+ }
+ }
+ }
+ if (style == SCE_C_OPERATOR) {
+ if (ch == '{') {
+ // Measure the minimum before a '{' to allow
+ // folding on "} else {"
+ if (levelMinCurrent > levelNext) {
+ levelMinCurrent = levelNext;
+ }
+ levelNext++;
+ } else if (ch == '}') {
+ levelNext--;
+ }
+ }
+ if (atEOL) {
+ int levelUse = levelCurrent;
+ if (foldAtElse) {
+ levelUse = levelMinCurrent;
+ }
+ int lev = levelUse | levelNext << 16;
+ if (visibleChars == 0 && foldCompact)
+ lev |= SC_FOLDLEVELWHITEFLAG;
+ if (levelUse < levelNext)
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ if (lev != styler.LevelAt(lineCurrent)) {
+ styler.SetLevel(lineCurrent, lev);
+ }
+ lineCurrent++;
+ levelCurrent = levelNext;
+ levelMinCurrent = levelCurrent;
+ visibleChars = 0;
+ }
+ if (!IsASpace(ch))
+ visibleChars++;
+ }
+}
+
+static void FoldCppDoc(unsigned int startPos, int length, int initStyle, WordList *[],
+ Accessor &styler) {
+ FoldNoBoxCppDoc(startPos, length, initStyle, styler);
+}
+
+static const char * const cppWordLists[] = {
+ "Primary keywords and identifiers",
+ "Secondary keywords and identifiers",
+ "Documentation comment keywords",
+ "Unused",
+ "Global classes and typedefs",
+ 0,
+ };
+
+static void ColouriseCppDocSensitive(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+ Accessor &styler) {
+ ColouriseCppDoc(startPos, length, initStyle, keywordlists, styler, true);
+}
+
+static void ColouriseCppDocInsensitive(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+ Accessor &styler) {
+ ColouriseCppDoc(startPos, length, initStyle, keywordlists, styler, false);
+}
+
+LexerModule lmCPP(SCLEX_CPP, ColouriseCppDocSensitive, "cpp", FoldCppDoc, cppWordLists);
+LexerModule lmCPPNoCase(SCLEX_CPPNOCASE, ColouriseCppDocInsensitive, "cppnocase", FoldCppDoc, cppWordLists);
diff --git a/src/LexCSS.cpp b/src/LexCSS.cpp
new file mode 100755
index 0000000..963a7b3
--- /dev/null
+++ b/src/LexCSS.cpp
@@ -0,0 +1,303 @@
+// Scintilla source code edit control
+/** @file LexCSS.cxx
+ ** Lexer for Cascading Style Sheets
+ ** Written by Jakub Vrána
+ ** Improved by Philippe Lhoste (CSS2)
+ **/
+// Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+
+static inline bool IsAWordChar(const unsigned int ch) {
+ return (isalnum(ch) || ch == '-' || ch == '_' || ch >= 161); // _ is not in fact correct CSS word-character
+}
+
+inline bool IsCssOperator(const char ch) {
+ if (!isalnum(ch) &&
+ (ch == '{' || ch == '}' || ch == ':' || ch == ',' || ch == ';' ||
+ ch == '.' || ch == '#' || ch == '!' || ch == '@' ||
+ /* CSS2 */
+ ch == '*' || ch == '>' || ch == '+' || ch == '=' || ch == '~' || ch == '|' ||
+ ch == '[' || ch == ']' || ch == '(' || ch == ')')) {
+ return true;
+ }
+ return false;
+}
+
+static void ColouriseCssDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[], Accessor &styler) {
+ WordList &keywords = *keywordlists[0];
+ WordList &pseudoClasses = *keywordlists[1];
+ WordList &keywords2 = *keywordlists[2];
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ int lastState = -1; // before operator
+ int lastStateC = -1; // before comment
+ int op = ' '; // last operator
+
+ for (; sc.More(); sc.Forward()) {
+ if (sc.state == SCE_CSS_COMMENT && sc.Match('*', '/')) {
+ if (lastStateC == -1) {
+ // backtrack to get last state:
+ // comments are like whitespace, so we must return to the previous state
+ unsigned int i = startPos;
+ for (; i > 0; i--) {
+ if ((lastStateC = styler.StyleAt(i-1)) != SCE_CSS_COMMENT) {
+ if (lastStateC == SCE_CSS_OPERATOR) {
+ op = styler.SafeGetCharAt(i-1);
+ while (--i) {
+ lastState = styler.StyleAt(i-1);
+ if (lastState != SCE_CSS_OPERATOR && lastState != SCE_CSS_COMMENT)
+ break;
+ }
+ if (i == 0)
+ lastState = SCE_CSS_DEFAULT;
+ }
+ break;
+ }
+ }
+ if (i == 0)
+ lastStateC = SCE_CSS_DEFAULT;
+ }
+ sc.Forward();
+ sc.ForwardSetState(lastStateC);
+ }
+
+ if (sc.state == SCE_CSS_COMMENT)
+ continue;
+
+ if (sc.state == SCE_CSS_DOUBLESTRING || sc.state == SCE_CSS_SINGLESTRING) {
+ if (sc.ch != (sc.state == SCE_CSS_DOUBLESTRING ? '\"' : '\''))
+ continue;
+ unsigned int i = sc.currentPos;
+ while (i && styler[i-1] == '\\')
+ i--;
+ if ((sc.currentPos - i) % 2 == 1)
+ continue;
+ sc.ForwardSetState(SCE_CSS_VALUE);
+ }
+
+ if (sc.state == SCE_CSS_OPERATOR) {
+ if (op == ' ') {
+ unsigned int i = startPos;
+ op = styler.SafeGetCharAt(i-1);
+ while (--i) {
+ lastState = styler.StyleAt(i-1);
+ if (lastState != SCE_CSS_OPERATOR && lastState != SCE_CSS_COMMENT)
+ break;
+ }
+ }
+ switch (op) {
+ case '@':
+ if (lastState == SCE_CSS_DEFAULT)
+ sc.SetState(SCE_CSS_DIRECTIVE);
+ break;
+ case '*':
+ if (lastState == SCE_CSS_DEFAULT)
+ sc.SetState(SCE_CSS_TAG);
+ break;
+ case '>':
+ case '+':
+ if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_CLASS
+ || lastState == SCE_CSS_ID || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS)
+ sc.SetState(SCE_CSS_DEFAULT);
+ break;
+ case '[':
+ if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_DEFAULT ||
+ lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS)
+ sc.SetState(SCE_CSS_ATTRIBUTE);
+ break;
+ case ']':
+ if (lastState == SCE_CSS_ATTRIBUTE)
+ sc.SetState(SCE_CSS_TAG);
+ break;
+ case '{':
+ if (lastState == SCE_CSS_DIRECTIVE)
+ sc.SetState(SCE_CSS_DEFAULT);
+ else if (lastState == SCE_CSS_TAG)
+ sc.SetState(SCE_CSS_IDENTIFIER);
+ break;
+ case '}':
+ if (lastState == SCE_CSS_DEFAULT || lastState == SCE_CSS_VALUE || lastState == SCE_CSS_IMPORTANT ||
+ lastState == SCE_CSS_IDENTIFIER || lastState == SCE_CSS_IDENTIFIER2)
+ sc.SetState(SCE_CSS_DEFAULT);
+ break;
+ case ':':
+ if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_DEFAULT ||
+ lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS)
+ sc.SetState(SCE_CSS_PSEUDOCLASS);
+ else if (lastState == SCE_CSS_IDENTIFIER || lastState == SCE_CSS_IDENTIFIER2 || lastState == SCE_CSS_UNKNOWN_IDENTIFIER)
+ sc.SetState(SCE_CSS_VALUE);
+ break;
+ case '.':
+ if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_DEFAULT ||
+ lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS)
+ sc.SetState(SCE_CSS_CLASS);
+ break;
+ case '#':
+ if (lastState == SCE_CSS_TAG || lastState == SCE_CSS_PSEUDOCLASS || lastState == SCE_CSS_DEFAULT ||
+ lastState == SCE_CSS_CLASS || lastState == SCE_CSS_ID || lastState == SCE_CSS_UNKNOWN_PSEUDOCLASS)
+ sc.SetState(SCE_CSS_ID);
+ break;
+ case ',':
+ if (lastState == SCE_CSS_TAG)
+ sc.SetState(SCE_CSS_DEFAULT);
+ break;
+ case ';':
+ if (lastState == SCE_CSS_DIRECTIVE)
+ sc.SetState(SCE_CSS_DEFAULT);
+ else if (lastState == SCE_CSS_VALUE || lastState == SCE_CSS_IMPORTANT)
+ sc.SetState(SCE_CSS_IDENTIFIER);
+ break;
+ case '!':
+ if (lastState == SCE_CSS_VALUE)
+ sc.SetState(SCE_CSS_IMPORTANT);
+ break;
+ }
+ }
+
+ if (IsAWordChar(sc.ch)) {
+ if (sc.state == SCE_CSS_DEFAULT)
+ sc.SetState(SCE_CSS_TAG);
+ continue;
+ }
+
+ if (IsAWordChar(sc.chPrev) && (
+ sc.state == SCE_CSS_IDENTIFIER || sc.state == SCE_CSS_IDENTIFIER2
+ || sc.state == SCE_CSS_UNKNOWN_IDENTIFIER
+ || sc.state == SCE_CSS_PSEUDOCLASS || sc.state == SCE_CSS_UNKNOWN_PSEUDOCLASS
+ || sc.state == SCE_CSS_IMPORTANT
+ )) {
+ char s[100];
+ sc.GetCurrentLowered(s, sizeof(s));
+ char *s2 = s;
+ while (*s2 && !IsAWordChar(*s2))
+ s2++;
+ switch (sc.state) {
+ case SCE_CSS_IDENTIFIER:
+ if (!keywords.InList(s2)) {
+ if (keywords2.InList(s2)) {
+ sc.ChangeState(SCE_CSS_IDENTIFIER2);
+ } else {
+ sc.ChangeState(SCE_CSS_UNKNOWN_IDENTIFIER);
+ }
+ }
+ break;
+ case SCE_CSS_UNKNOWN_IDENTIFIER:
+ if (keywords.InList(s2))
+ sc.ChangeState(SCE_CSS_IDENTIFIER);
+ else if (keywords2.InList(s2))
+ sc.ChangeState(SCE_CSS_IDENTIFIER2);
+ break;
+ case SCE_CSS_PSEUDOCLASS:
+ if (!pseudoClasses.InList(s2))
+ sc.ChangeState(SCE_CSS_UNKNOWN_PSEUDOCLASS);
+ break;
+ case SCE_CSS_UNKNOWN_PSEUDOCLASS:
+ if (pseudoClasses.InList(s2))
+ sc.ChangeState(SCE_CSS_PSEUDOCLASS);
+ break;
+ case SCE_CSS_IMPORTANT:
+ if (strcmp(s2, "important") != 0)
+ sc.ChangeState(SCE_CSS_VALUE);
+ break;
+ }
+ }
+
+ if (sc.ch != '.' && sc.ch != ':' && sc.ch != '#' && (sc.state == SCE_CSS_CLASS || sc.state == SCE_CSS_PSEUDOCLASS || sc.state == SCE_CSS_UNKNOWN_PSEUDOCLASS || sc.state == SCE_CSS_ID))
+ sc.SetState(SCE_CSS_TAG);
+
+ if (sc.Match('/', '*')) {
+ lastStateC = sc.state;
+ sc.SetState(SCE_CSS_COMMENT);
+ sc.Forward();
+ } else if (sc.state == SCE_CSS_VALUE && (sc.ch == '\"' || sc.ch == '\'')) {
+ sc.SetState((sc.ch == '\"' ? SCE_CSS_DOUBLESTRING : SCE_CSS_SINGLESTRING));
+ } else if (IsCssOperator(static_cast<char>(sc.ch))
+ && (sc.state != SCE_CSS_ATTRIBUTE || sc.ch == ']')
+ && (sc.state != SCE_CSS_VALUE || sc.ch == ';' || sc.ch == '}' || sc.ch == '!')
+ && (sc.state != SCE_CSS_DIRECTIVE || sc.ch == ';' || sc.ch == '{')
+ ) {
+ if (sc.state != SCE_CSS_OPERATOR)
+ lastState = sc.state;
+ sc.SetState(SCE_CSS_OPERATOR);
+ op = sc.ch;
+ }
+ }
+
+ sc.Complete();
+}
+
+static void FoldCSSDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
+ bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ unsigned int endPos = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
+ int levelCurrent = levelPrev;
+ char chNext = styler[startPos];
+ bool inComment = (styler.StyleAt(startPos-1) == SCE_CSS_COMMENT);
+ for (unsigned int i = startPos; i < endPos; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ int style = styler.StyleAt(i);
+ bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
+ if (foldComment) {
+ if (!inComment && (style == SCE_CSS_COMMENT))
+ levelCurrent++;
+ else if (inComment && (style != SCE_CSS_COMMENT))
+ levelCurrent--;
+ inComment = (style == SCE_CSS_COMMENT);
+ }
+ if (style == SCE_CSS_OPERATOR) {
+ if (ch == '{') {
+ levelCurrent++;
+ } else if (ch == '}') {
+ levelCurrent--;
+ }
+ }
+ if (atEOL) {
+ int lev = levelPrev;
+ if (visibleChars == 0 && foldCompact)
+ 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 cssWordListDesc[] = {
+ "CSS1 Keywords",
+ "Pseudo classes",
+ "CSS2 Keywords",
+ 0
+};
+
+LexerModule lmCss(SCLEX_CSS, ColouriseCssDoc, "css", FoldCSSDoc, cssWordListDesc);
diff --git a/src/LexCaml.cpp b/src/LexCaml.cpp
new file mode 100644
index 0000000..5f4fad5
--- /dev/null
+++ b/src/LexCaml.cpp
@@ -0,0 +1,399 @@
+// Scintilla source code edit control
+/** @file LexCaml.cxx
+ ** Lexer for Objective Caml.
+ **/
+// Copyright 2005 by Robert Roessler <robertr@rftp.com>
+// The License.txt file describes the conditions under which this software may be distributed.
+/* Release History
+ 20050204 Initial release.
+ 20050205 Quick compiler standards/"cleanliness" adjustment.
+ 20050206 Added cast for IsLeadByte().
+ 20050209 Changes to "external" build support.
+ 20050306 Fix for 1st-char-in-doc "corner" case.
+ 20050502 Fix for [harmless] one-past-the-end coloring.
+ 20050515 Refined numeric token recognition logic.
+ 20051125 Added 2nd "optional" keywords class.
+ 20051129 Support "magic" (read-only) comments for RCaml.
+ 20051204 Swtich to using StyleContext infrastructure.
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+// Since the Microsoft __iscsym[f] funcs are not ANSI...
+inline int iscaml(int c) {return isalnum(c) || c == '_';}
+inline int iscamlf(int c) {return isalpha(c) || c == '_';}
+inline int iscamld(int c) {return isdigit(c) || c == '_';}
+
+static const int baseT[24] = {
+ 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* A - L */
+ 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0,16 /* M - X */
+};
+
+#ifdef BUILD_AS_EXTERNAL_LEXER
+/*
+ (actually seems to work!)
+*/
+#include "WindowAccessor.h"
+#include "ExternalLexer.h"
+
+#if PLAT_WIN
+#include <windows.h>
+#endif
+
+static void ColouriseCamlDoc(
+ unsigned int startPos, int length,
+ int initStyle,
+ WordList *keywordlists[],
+ Accessor &styler);
+
+static void FoldCamlDoc(
+ unsigned int startPos, int length,
+ int initStyle,
+ WordList *keywordlists[],
+ Accessor &styler);
+
+static void InternalLexOrFold(int lexOrFold, unsigned int startPos, int length,
+ int initStyle, char *words[], WindowID window, char *props);
+
+static const char* LexerName = "caml";
+
+#ifdef TRACE
+void Platform::DebugPrintf(const char *format, ...) {
+ char buffer[2000];
+ va_list pArguments;
+ va_start(pArguments, format);
+ vsprintf(buffer,format,pArguments);
+ va_end(pArguments);
+ Platform::DebugDisplay(buffer);
+}
+#else
+void Platform::DebugPrintf(const char *, ...) {
+}
+#endif
+
+bool Platform::IsDBCSLeadByte(int codePage, char ch) {
+ return ::IsDBCSLeadByteEx(codePage, ch) != 0;
+}
+
+long Platform::SendScintilla(WindowID w, unsigned int msg, unsigned long wParam, long lParam) {
+ return ::SendMessage(reinterpret_cast<HWND>(w), msg, wParam, lParam);
+}
+
+long Platform::SendScintillaPointer(WindowID w, unsigned int msg, unsigned long wParam, void *lParam) {
+ return ::SendMessage(reinterpret_cast<HWND>(w), msg, wParam,
+ reinterpret_cast<LPARAM>(lParam));
+}
+
+void EXT_LEXER_DECL Fold(unsigned int lexer, unsigned int startPos, int length,
+ int initStyle, char *words[], WindowID window, char *props)
+{
+ // below useless evaluation(s) to supress "not used" warnings
+ lexer;
+ // build expected data structures and do the Fold
+ InternalLexOrFold(1, startPos, length, initStyle, words, window, props);
+
+}
+
+int EXT_LEXER_DECL GetLexerCount()
+{
+ return 1; // just us [Objective] Caml lexers here!
+}
+
+void EXT_LEXER_DECL GetLexerName(unsigned int Index, char *name, int buflength)
+{
+ // below useless evaluation(s) to supress "not used" warnings
+ Index;
+ // return as much of our lexer name as will fit (what's up with Index?)
+ if (buflength > 0) {
+ buflength--;
+ int n = strlen(LexerName);
+ if (n > buflength)
+ n = buflength;
+ memcpy(name, LexerName, n), name[n] = '\0';
+ }
+}
+
+void EXT_LEXER_DECL Lex(unsigned int lexer, unsigned int startPos, int length,
+ int initStyle, char *words[], WindowID window, char *props)
+{
+ // below useless evaluation(s) to supress "not used" warnings
+ lexer;
+ // build expected data structures and do the Lex
+ InternalLexOrFold(0, startPos, length, initStyle, words, window, props);
+}
+
+static void InternalLexOrFold(int foldOrLex, unsigned int startPos, int length,
+ int initStyle, char *words[], WindowID window, char *props)
+{
+ // create and initialize a WindowAccessor (including contained PropSet)
+ PropSet ps;
+ ps.SetMultiple(props);
+ WindowAccessor wa(window, ps);
+ // create and initialize WordList(s)
+ int nWL = 0;
+ for (; words[nWL]; nWL++) ; // count # of WordList PTRs needed
+ WordList** wl = new WordList* [nWL + 1];// alloc WordList PTRs
+ int i = 0;
+ for (; i < nWL; i++) {
+ wl[i] = new WordList(); // (works or THROWS bad_alloc EXCEPTION)
+ wl[i]->Set(words[i]);
+ }
+ wl[i] = 0;
+ // call our "internal" folder/lexer (... then do Flush!)
+ if (foldOrLex)
+ FoldCamlDoc(startPos, length, initStyle, wl, wa);
+ else
+ ColouriseCamlDoc(startPos, length, initStyle, wl, wa);
+ wa.Flush();
+ // clean up before leaving
+ for (i = nWL - 1; i >= 0; i--)
+ delete wl[i];
+ delete [] wl;
+}
+
+static
+#endif /* BUILD_AS_EXTERNAL_LEXER */
+
+void ColouriseCamlDoc(
+ unsigned int startPos, int length,
+ int initStyle,
+ WordList *keywordlists[],
+ Accessor &styler)
+{
+ // initialize styler
+ StyleContext sc(startPos, length, initStyle, styler);
+ // set up [initial] state info (terminating states that shouldn't "bleed")
+ int nesting = 0;
+ if (sc.state < SCE_CAML_STRING)
+ sc.state = SCE_CAML_DEFAULT;
+ if (sc.state >= SCE_CAML_COMMENT)
+ nesting = (sc.state & 0x0f) - SCE_CAML_COMMENT;
+
+ int chBase = 0, chToken = 0, chLit = 0;
+ WordList& keywords = *keywordlists[0];
+ WordList& keywords2 = *keywordlists[1];
+ WordList& keywords3 = *keywordlists[2];
+ const int useMagic = styler.GetPropertyInt("lexer.caml.magic", 0);
+
+ // foreach char in range...
+ while (sc.More()) {
+ // set up [per-char] state info
+ int state2 = -1; // (ASSUME no state change)
+ int chColor = sc.currentPos - 1;// (ASSUME standard coloring range)
+ bool advance = true; // (ASSUME scanner "eats" 1 char)
+
+ // step state machine
+ switch (sc.state & 0x0f) {
+ case SCE_CAML_DEFAULT:
+ chToken = sc.currentPos; // save [possible] token start (JIC)
+ // it's wide open; what do we have?
+ if (iscamlf(sc.ch))
+ state2 = SCE_CAML_IDENTIFIER;
+ else if (sc.Match('`') && iscamlf(sc.chNext))
+ state2 = SCE_CAML_TAGNAME;
+ else if (sc.Match('#') && isdigit(sc.chNext))
+ state2 = SCE_CAML_LINENUM;
+ else if (isdigit(sc.ch)) {
+ state2 = SCE_CAML_NUMBER, chBase = 10;
+ if (sc.Match('0') && strchr("bBoOxX", sc.chNext))
+ chBase = baseT[tolower(sc.chNext) - 'a'], sc.Forward();
+ } else if (sc.Match('\'')) /* (char literal?) */
+ state2 = SCE_CAML_CHAR, chLit = 0;
+ else if (sc.Match('\"'))
+ state2 = SCE_CAML_STRING;
+ else if (sc.Match('(', '*'))
+ state2 = SCE_CAML_COMMENT,
+ sc.ch = ' ', // (make SURE "(*)" isn't seen as a closed comment)
+ sc.Forward();
+ else if (strchr("!?~" /* Caml "prefix-symbol" */
+ "=<>@^|&+-*/$%" /* Caml "infix-symbol" */
+ "()[]{};,:.#", sc.ch)) /* Caml "bracket" or ;,:.# */
+ state2 = SCE_CAML_OPERATOR;
+ break;
+
+ case SCE_CAML_IDENTIFIER:
+ // [try to] interpret as [additional] identifier char
+ if (!(iscaml(sc.ch) || sc.Match('\''))) {
+ const int n = sc.currentPos - chToken;
+ if (n < 24) {
+ // length is believable as keyword, [re-]construct token
+ char t[24];
+ for (int i = -n; i < 0; i++)
+ t[n + i] = static_cast<char>(sc.GetRelative(i));
+ t[n] = '\0';
+ // special-case "_" token as KEYWORD
+ if ((n == 1 && sc.chPrev == '_') || keywords.InList(t))
+ sc.ChangeState(SCE_CAML_KEYWORD);
+ else if (keywords2.InList(t))
+ sc.ChangeState(SCE_CAML_KEYWORD2);
+ else if (keywords3.InList(t))
+ sc.ChangeState(SCE_CAML_KEYWORD3);
+ }
+ state2 = SCE_CAML_DEFAULT, advance = false;
+ }
+ break;
+
+ case SCE_CAML_TAGNAME:
+ // [try to] interpret as [additional] tagname char
+ if (!(iscaml(sc.ch) || sc.Match('\'')))
+ state2 = SCE_CAML_DEFAULT, advance = false;
+ break;
+
+ /*case SCE_CAML_KEYWORD:
+ case SCE_CAML_KEYWORD2:
+ case SCE_CAML_KEYWORD3:
+ // [try to] interpret as [additional] keyword char
+ if (!iscaml(ch))
+ state2 = SCE_CAML_DEFAULT, advance = false;
+ break;*/
+
+ case SCE_CAML_LINENUM:
+ // [try to] interpret as [additional] linenum directive char
+ if (!isdigit(sc.ch))
+ state2 = SCE_CAML_DEFAULT, advance = false;
+ break;
+
+ case SCE_CAML_OPERATOR: {
+ // [try to] interpret as [additional] operator char
+ const char* o = 0;
+ if (iscaml(sc.ch) || isspace(sc.ch) /* ident or whitespace */
+ || (o = strchr(")]};,\'\"`#", sc.ch),o)/* "termination" chars */
+ || !strchr("!$%&*+-./:<=>?@^|~", sc.ch)/* "operator" chars */) {
+ // check for INCLUSIVE termination
+ if (o && strchr(")]};,", sc.ch)) {
+ if ((sc.Match(')') && sc.chPrev == '(')
+ || (sc.Match(']') && sc.chPrev == '['))
+ // special-case "()" and "[]" tokens as KEYWORDS
+ sc.ChangeState(SCE_CAML_KEYWORD);
+ chColor++;
+ } else
+ advance = false;
+ state2 = SCE_CAML_DEFAULT;
+ }
+ break;
+ }
+
+ case SCE_CAML_NUMBER:
+ // [try to] interpret as [additional] numeric literal char
+ // N.B. - improperly accepts "extra" digits in base 2 or 8 literals
+ if (iscamld(sc.ch) || IsADigit(sc.ch, chBase))
+ break;
+ // how about an integer suffix?
+ if ((sc.Match('l') || sc.Match('L') || sc.Match('n'))
+ && (iscamld(sc.chPrev) || IsADigit(sc.chPrev, chBase)))
+ break;
+ // or a floating-point literal?
+ if (chBase == 10) {
+ // with a decimal point?
+ if (sc.Match('.') && iscamld(sc.chPrev))
+ break;
+ // with an exponent? (I)
+ if ((sc.Match('e') || sc.Match('E'))
+ && (iscamld(sc.chPrev) || sc.chPrev == '.'))
+ break;
+ // with an exponent? (II)
+ if ((sc.Match('+') || sc.Match('-'))
+ && (sc.chPrev == 'e' || sc.chPrev == 'E'))
+ break;
+ }
+ // it looks like we have run out of number
+ state2 = SCE_CAML_DEFAULT, advance = false;
+ break;
+
+ case SCE_CAML_CHAR:
+ // [try to] interpret as [additional] char literal char
+ if (sc.Match('\\')) {
+ chLit = 1; // (definitely IS a char literal)
+ if (sc.chPrev == '\\')
+ sc.ch = ' '; // (so termination test isn't fooled)
+ // should we be terminating - one way or another?
+ } else if ((sc.Match('\'') && sc.chPrev != '\\') || sc.atLineEnd) {
+ state2 = SCE_CAML_DEFAULT;
+ if (sc.Match('\''))
+ chColor++;
+ else
+ sc.ChangeState(SCE_CAML_IDENTIFIER);
+ // ... maybe a char literal, maybe not
+ } else if (chLit < 1 && sc.currentPos - chToken >= 2)
+ sc.ChangeState(SCE_CAML_IDENTIFIER), advance = false;
+ break;
+
+ case SCE_CAML_STRING:
+ // [try to] interpret as [additional] string literal char
+ if (sc.Match('\\') && sc.chPrev == '\\')
+ sc.ch = ' '; // (so '\\' doesn't cause us trouble)
+ else if (sc.Match('\"') && sc.chPrev != '\\')
+ state2 = SCE_CAML_DEFAULT, chColor++;
+ break;
+
+ case SCE_CAML_COMMENT:
+ case SCE_CAML_COMMENT1:
+ case SCE_CAML_COMMENT2:
+ case SCE_CAML_COMMENT3:
+ // we're IN a comment - does this start a NESTED comment?
+ if (sc.Match('(', '*'))
+ state2 = sc.state + 1, chToken = sc.currentPos,
+ sc.ch = ' ', // (make SURE "(*)" isn't seen as a closed comment)
+ sc.Forward(), nesting++;
+ // [try to] interpret as [additional] comment char
+ else if (sc.Match(')') && sc.chPrev == '*') {
+ if (nesting)
+ state2 = (sc.state & 0x0f) - 1, chToken = 0, nesting--;
+ else
+ state2 = SCE_CAML_DEFAULT;
+ chColor++;
+ // enable "magic" (read-only) comment AS REQUIRED
+ } else if (useMagic && sc.currentPos - chToken == 4
+ && sc.Match('c') && sc.chPrev == 'r' && sc.GetRelative(-2) == '@')
+ sc.state |= 0x10; // (switch to read-only comment style)
+ break;
+ }
+
+ // handle state change and char coloring as required
+ if (state2 >= 0)
+ styler.ColourTo(chColor, sc.state), sc.ChangeState(state2);
+ // move to next char UNLESS re-scanning current char
+ if (advance)
+ sc.Forward();
+ }
+
+ // do any required terminal char coloring (JIC)
+ sc.Complete();
+}
+
+#ifdef BUILD_AS_EXTERNAL_LEXER
+static
+#endif /* BUILD_AS_EXTERNAL_LEXER */
+void FoldCamlDoc(
+ unsigned int startPos, int length,
+ int initStyle,
+ WordList *keywordlists[],
+ Accessor &styler)
+{
+ // below useless evaluation(s) to supress "not used" warnings
+ startPos || length || initStyle || keywordlists[0] || styler.Length();
+}
+
+static const char * const camlWordListDesc[] = {
+ "Keywords", // primary Objective Caml keywords
+ "Keywords2", // "optional" keywords (typically from Pervasives)
+ "Keywords3", // "optional" keywords (typically typenames)
+ 0
+};
+
+#ifndef BUILD_AS_EXTERNAL_LEXER
+LexerModule lmCaml(SCLEX_CAML, ColouriseCamlDoc, "caml", FoldCamlDoc, camlWordListDesc);
+#endif /* BUILD_AS_EXTERNAL_LEXER */
diff --git a/src/LexConf.cpp b/src/LexConf.cpp
new file mode 100755
index 0000000..c33cdb5
--- /dev/null
+++ b/src/LexConf.cpp
@@ -0,0 +1,184 @@
+// Scintilla source code edit control
+/** @file LexConf.cxx
+ ** Lexer for Apache Configuration Files.
+ **
+ ** First working version contributed by Ahmad Zawawi <zeus_go64@hotmail.com> on October 28, 2000.
+ ** i created this lexer because i needed something pretty when dealing
+ ** when Apache Configuration files...
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+static void ColouriseConfDoc(unsigned int startPos, int length, int, WordList *keywordLists[], Accessor &styler)
+{
+ int state = SCE_CONF_DEFAULT;
+ char chNext = styler[startPos];
+ int lengthDoc = startPos + length;
+ // create a buffer large enough to take the largest chunk...
+ char *buffer = new char[length];
+ int bufferCount = 0;
+
+ // this assumes that we have 2 keyword list in conf.properties
+ WordList &directives = *keywordLists[0];
+ WordList &params = *keywordLists[1];
+
+ // go through all provided text segment
+ // using the hand-written state machine shown below
+ styler.StartAt(startPos);
+ styler.StartSegment(startPos);
+ for (int i = startPos; i < lengthDoc; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+
+ if (styler.IsLeadByte(ch)) {
+ chNext = styler.SafeGetCharAt(i + 2);
+ i++;
+ continue;
+ }
+ switch(state) {
+ case SCE_CONF_DEFAULT:
+ if( ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ') {
+ // whitespace is simply ignored here...
+ styler.ColourTo(i,SCE_CONF_DEFAULT);
+ break;
+ } else if( ch == '#' ) {
+ // signals the start of a comment...
+ state = SCE_CONF_COMMENT;
+ styler.ColourTo(i,SCE_CONF_COMMENT);
+ } else if( ch == '.' /*|| ch == '/'*/) {
+ // signals the start of a file...
+ state = SCE_CONF_EXTENSION;
+ styler.ColourTo(i,SCE_CONF_EXTENSION);
+ } else if( ch == '"') {
+ state = SCE_CONF_STRING;
+ styler.ColourTo(i,SCE_CONF_STRING);
+ } else if( ispunct(ch) ) {
+ // signals an operator...
+ // no state jump necessary for this
+ // simple case...
+ styler.ColourTo(i,SCE_CONF_OPERATOR);
+ } else if( isalpha(ch) ) {
+ // signals the start of an identifier
+ bufferCount = 0;
+ buffer[bufferCount++] = static_cast<char>(tolower(ch));
+ state = SCE_CONF_IDENTIFIER;
+ } else if( isdigit(ch) ) {
+ // signals the start of a number
+ bufferCount = 0;
+ buffer[bufferCount++] = ch;
+ //styler.ColourTo(i,SCE_CONF_NUMBER);
+ state = SCE_CONF_NUMBER;
+ } else {
+ // style it the default style..
+ styler.ColourTo(i,SCE_CONF_DEFAULT);
+ }
+ break;
+
+ case SCE_CONF_COMMENT:
+ // if we find a newline here,
+ // we simply go to default state
+ // else continue to work on it...
+ if( ch == '\n' || ch == '\r' ) {
+ state = SCE_CONF_DEFAULT;
+ } else {
+ styler.ColourTo(i,SCE_CONF_COMMENT);
+ }
+ break;
+
+ case SCE_CONF_EXTENSION:
+ // if we find a non-alphanumeric char,
+ // we simply go to default state
+ // else we're still dealing with an extension...
+ if( isalnum(ch) || (ch == '_') ||
+ (ch == '-') || (ch == '$') ||
+ (ch == '/') || (ch == '.') || (ch == '*') )
+ {
+ styler.ColourTo(i,SCE_CONF_EXTENSION);
+ } else {
+ state = SCE_CONF_DEFAULT;
+ chNext = styler[i--];
+ }
+ break;
+
+ case SCE_CONF_STRING:
+ // if we find the end of a string char, we simply go to default state
+ // else we're still dealing with an string...
+ if( (ch == '"' && styler.SafeGetCharAt(i-1)!='\\') || (ch == '\n') || (ch == '\r') ) {
+ state = SCE_CONF_DEFAULT;
+ }
+ styler.ColourTo(i,SCE_CONF_STRING);
+ break;
+
+ case SCE_CONF_IDENTIFIER:
+ // stay in CONF_IDENTIFIER state until we find a non-alphanumeric
+ if( isalnum(ch) || (ch == '_') || (ch == '-') || (ch == '/') || (ch == '$') || (ch == '.') || (ch == '*')) {
+ buffer[bufferCount++] = static_cast<char>(tolower(ch));
+ } else {
+ state = SCE_CONF_DEFAULT;
+ buffer[bufferCount] = '\0';
+
+ // check if the buffer contains a keyword, and highlight it if it is a keyword...
+ if(directives.InList(buffer)) {
+ styler.ColourTo(i-1,SCE_CONF_DIRECTIVE );
+ } else if(params.InList(buffer)) {
+ styler.ColourTo(i-1,SCE_CONF_PARAMETER );
+ } else if(strchr(buffer,'/') || strchr(buffer,'.')) {
+ styler.ColourTo(i-1,SCE_CONF_EXTENSION);
+ } else {
+ styler.ColourTo(i-1,SCE_CONF_DEFAULT);
+ }
+
+ // push back the faulty character
+ chNext = styler[i--];
+
+ }
+ break;
+
+ case SCE_CONF_NUMBER:
+ // stay in CONF_NUMBER state until we find a non-numeric
+ if( isdigit(ch) || ch == '.') {
+ buffer[bufferCount++] = ch;
+ } else {
+ state = SCE_CONF_DEFAULT;
+ buffer[bufferCount] = '\0';
+
+ // Colourize here...
+ if( strchr(buffer,'.') ) {
+ // it is an IP address...
+ styler.ColourTo(i-1,SCE_CONF_IP);
+ } else {
+ // normal number
+ styler.ColourTo(i-1,SCE_CONF_NUMBER);
+ }
+
+ // push back a character
+ chNext = styler[i--];
+ }
+ break;
+
+ }
+ }
+ delete []buffer;
+}
+
+static const char * const confWordListDesc[] = {
+ "Directives",
+ "Parameters",
+ 0
+};
+
+LexerModule lmConf(SCLEX_CONF, ColouriseConfDoc, "conf", 0, confWordListDesc);
diff --git a/src/LexCrontab.cpp b/src/LexCrontab.cpp
new file mode 100755
index 0000000..d139bb4
--- /dev/null
+++ b/src/LexCrontab.cpp
@@ -0,0 +1,218 @@
+// Scintilla source code edit control
+/** @file LexCrontab.cxx
+ ** Lexer to use with extended crontab files used by a powerful
+ ** Windows scheduler/event monitor/automation manager nnCron.
+ ** (http://nemtsev.eserv.ru/)
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+static void ColouriseNncrontabDoc(unsigned int startPos, int length, int, WordList
+*keywordLists[], Accessor &styler)
+{
+ int state = SCE_NNCRONTAB_DEFAULT;
+ char chNext = styler[startPos];
+ int lengthDoc = startPos + length;
+ // create a buffer large enough to take the largest chunk...
+ char *buffer = new char[length];
+ int bufferCount = 0;
+ // used when highliting environment variables inside quoted string:
+ bool insideString = false;
+
+ // this assumes that we have 3 keyword list in conf.properties
+ WordList &section = *keywordLists[0];
+ WordList &keyword = *keywordLists[1];
+ WordList &modifier = *keywordLists[2];
+
+ // go through all provided text segment
+ // using the hand-written state machine shown below
+ styler.StartAt(startPos);
+ styler.StartSegment(startPos);
+ for (int i = startPos; i < lengthDoc; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+
+ if (styler.IsLeadByte(ch)) {
+ chNext = styler.SafeGetCharAt(i + 2);
+ i++;
+ continue;
+ }
+ switch(state) {
+ case SCE_NNCRONTAB_DEFAULT:
+ if( ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ') {
+ // whitespace is simply ignored here...
+ styler.ColourTo(i,SCE_NNCRONTAB_DEFAULT);
+ break;
+ } else if( ch == '#' && styler.SafeGetCharAt(i+1) == '(') {
+ // signals the start of a task...
+ state = SCE_NNCRONTAB_TASK;
+ styler.ColourTo(i,SCE_NNCRONTAB_TASK);
+ }
+ else if( ch == '\\' && (styler.SafeGetCharAt(i+1) == ' ' ||
+ styler.SafeGetCharAt(i+1) == '\t')) {
+ // signals the start of an extended comment...
+ state = SCE_NNCRONTAB_COMMENT;
+ styler.ColourTo(i,SCE_NNCRONTAB_COMMENT);
+ } else if( ch == '#' ) {
+ // signals the start of a plain comment...
+ state = SCE_NNCRONTAB_COMMENT;
+ styler.ColourTo(i,SCE_NNCRONTAB_COMMENT);
+ } else if( ch == ')' && styler.SafeGetCharAt(i+1) == '#') {
+ // signals the end of a task...
+ state = SCE_NNCRONTAB_TASK;
+ styler.ColourTo(i,SCE_NNCRONTAB_TASK);
+ } else if( ch == '"') {
+ state = SCE_NNCRONTAB_STRING;
+ styler.ColourTo(i,SCE_NNCRONTAB_STRING);
+ } else if( ch == '%') {
+ // signals environment variables
+ state = SCE_NNCRONTAB_ENVIRONMENT;
+ styler.ColourTo(i,SCE_NNCRONTAB_ENVIRONMENT);
+ } else if( ch == '<' && styler.SafeGetCharAt(i+1) == '%') {
+ // signals environment variables
+ state = SCE_NNCRONTAB_ENVIRONMENT;
+ styler.ColourTo(i,SCE_NNCRONTAB_ENVIRONMENT);
+ } else if( ch == '*' ) {
+ // signals an asterisk
+ // no state jump necessary for this simple case...
+ styler.ColourTo(i,SCE_NNCRONTAB_ASTERISK);
+ } else if( isalpha(ch) || ch == '<' ) {
+ // signals the start of an identifier
+ bufferCount = 0;
+ buffer[bufferCount++] = ch;
+ state = SCE_NNCRONTAB_IDENTIFIER;
+ } else if( isdigit(ch) ) {
+ // signals the start of a number
+ bufferCount = 0;
+ buffer[bufferCount++] = ch;
+ state = SCE_NNCRONTAB_NUMBER;
+ } else {
+ // style it the default style..
+ styler.ColourTo(i,SCE_NNCRONTAB_DEFAULT);
+ }
+ break;
+
+ case SCE_NNCRONTAB_COMMENT:
+ // if we find a newline here,
+ // we simply go to default state
+ // else continue to work on it...
+ if( ch == '\n' || ch == '\r' ) {
+ state = SCE_NNCRONTAB_DEFAULT;
+ } else {
+ styler.ColourTo(i,SCE_NNCRONTAB_COMMENT);
+ }
+ break;
+
+ case SCE_NNCRONTAB_TASK:
+ // if we find a newline here,
+ // we simply go to default state
+ // else continue to work on it...
+ if( ch == '\n' || ch == '\r' ) {
+ state = SCE_NNCRONTAB_DEFAULT;
+ } else {
+ styler.ColourTo(i,SCE_NNCRONTAB_TASK);
+ }
+ break;
+
+ case SCE_NNCRONTAB_STRING:
+ if( ch == '%' ) {
+ state = SCE_NNCRONTAB_ENVIRONMENT;
+ insideString = true;
+ styler.ColourTo(i-1,SCE_NNCRONTAB_STRING);
+ break;
+ }
+ // if we find the end of a string char, we simply go to default state
+ // else we're still dealing with an string...
+ if( (ch == '"' && styler.SafeGetCharAt(i-1)!='\\') ||
+ (ch == '\n') || (ch == '\r') ) {
+ state = SCE_NNCRONTAB_DEFAULT;
+ }
+ styler.ColourTo(i,SCE_NNCRONTAB_STRING);
+ break;
+
+ case SCE_NNCRONTAB_ENVIRONMENT:
+ // if we find the end of a string char, we simply go to default state
+ // else we're still dealing with an string...
+ if( ch == '%' && insideString ) {
+ state = SCE_NNCRONTAB_STRING;
+ insideString = false;
+ break;
+ }
+ if( (ch == '%' && styler.SafeGetCharAt(i-1)!='\\')
+ || (ch == '\n') || (ch == '\r') || (ch == '>') ) {
+ state = SCE_NNCRONTAB_DEFAULT;
+ styler.ColourTo(i,SCE_NNCRONTAB_ENVIRONMENT);
+ break;
+ }
+ styler.ColourTo(i+1,SCE_NNCRONTAB_ENVIRONMENT);
+ break;
+
+ case SCE_NNCRONTAB_IDENTIFIER:
+ // stay in CONF_IDENTIFIER state until we find a non-alphanumeric
+ if( isalnum(ch) || (ch == '_') || (ch == '-') || (ch == '/') ||
+ (ch == '$') || (ch == '.') || (ch == '<') || (ch == '>') ||
+ (ch == '@') ) {
+ buffer[bufferCount++] = ch;
+ } else {
+ state = SCE_NNCRONTAB_DEFAULT;
+ buffer[bufferCount] = '\0';
+
+ // check if the buffer contains a keyword,
+ // and highlight it if it is a keyword...
+ if(section.InList(buffer)) {
+ styler.ColourTo(i,SCE_NNCRONTAB_SECTION );
+ } else if(keyword.InList(buffer)) {
+ styler.ColourTo(i-1,SCE_NNCRONTAB_KEYWORD );
+ } // else if(strchr(buffer,'/') || strchr(buffer,'.')) {
+ // styler.ColourTo(i-1,SCE_NNCRONTAB_EXTENSION);
+ // }
+ else if(modifier.InList(buffer)) {
+ styler.ColourTo(i-1,SCE_NNCRONTAB_MODIFIER );
+ } else {
+ styler.ColourTo(i-1,SCE_NNCRONTAB_DEFAULT);
+ }
+ // push back the faulty character
+ chNext = styler[i--];
+ }
+ break;
+
+ case SCE_NNCRONTAB_NUMBER:
+ // stay in CONF_NUMBER state until we find a non-numeric
+ if( isdigit(ch) /* || ch == '.' */ ) {
+ buffer[bufferCount++] = ch;
+ } else {
+ state = SCE_NNCRONTAB_DEFAULT;
+ buffer[bufferCount] = '\0';
+ // Colourize here... (normal number)
+ styler.ColourTo(i-1,SCE_NNCRONTAB_NUMBER);
+ // push back a character
+ chNext = styler[i--];
+ }
+ break;
+ }
+ }
+ delete []buffer;
+}
+
+static const char * const cronWordListDesc[] = {
+ "Section keywords and Forth words",
+ "nnCrontab keywords",
+ "Modifiers",
+ 0
+};
+
+LexerModule lmNncrontab(SCLEX_NNCRONTAB, ColouriseNncrontabDoc, "nncrontab", 0, cronWordListDesc);
diff --git a/src/LexCsound.cpp b/src/LexCsound.cpp
new file mode 100644
index 0000000..27f7b99
--- /dev/null
+++ b/src/LexCsound.cpp
@@ -0,0 +1,207 @@
+// Scintilla source code edit control
+/** @file LexCsound.cxx
+ ** Lexer for Csound (Orchestra & Score)
+ ** Written by Georg Ritter - <ritterfuture A T gmail D O T com>
+ **/
+// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+
+static inline bool IsAWordChar(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '.' ||
+ ch == '_' || ch == '?');
+}
+
+static inline bool IsAWordStart(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '.' ||
+ ch == '%' || ch == '@' || ch == '$' || ch == '?');
+}
+
+static inline bool IsCsoundOperator(char ch) {
+ if (isalnum(ch))
+ return false;
+ // '.' left out as it is used to make up numbers
+ if (ch == '*' || ch == '/' || ch == '-' || ch == '+' ||
+ ch == '(' || ch == ')' || ch == '=' || ch == '^' ||
+ ch == '[' || ch == ']' || ch == '<' || ch == '&' ||
+ ch == '>' || ch == ',' || ch == '|' || ch == '~' ||
+ ch == '%' || ch == ':')
+ return true;
+ return false;
+}
+
+static void ColouriseCsoundDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+ Accessor &styler) {
+
+ WordList &opcode = *keywordlists[0];
+ WordList &headerStmt = *keywordlists[1];
+ WordList &otherKeyword = *keywordlists[2];
+
+ // Do not leak onto next line
+ if (initStyle == SCE_CSOUND_STRINGEOL)
+ initStyle = SCE_CSOUND_DEFAULT;
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ for (; sc.More(); sc.Forward())
+ {
+ // Handle line continuation generically.
+ if (sc.ch == '\\') {
+ if (sc.chNext == '\n' || sc.chNext == '\r') {
+ sc.Forward();
+ if (sc.ch == '\r' && sc.chNext == '\n') {
+ sc.Forward();
+ }
+ continue;
+ }
+ }
+
+ // Determine if the current state should terminate.
+ if (sc.state == SCE_CSOUND_OPERATOR) {
+ if (!IsCsoundOperator(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_CSOUND_DEFAULT);
+ }
+ }else if (sc.state == SCE_CSOUND_NUMBER) {
+ if (!IsAWordChar(sc.ch)) {
+ sc.SetState(SCE_CSOUND_DEFAULT);
+ }
+ } else if (sc.state == SCE_CSOUND_IDENTIFIER) {
+ if (!IsAWordChar(sc.ch) ) {
+ char s[100];
+ sc.GetCurrent(s, sizeof(s));
+
+ if (opcode.InList(s)) {
+ sc.ChangeState(SCE_CSOUND_OPCODE);
+ } else if (headerStmt.InList(s)) {
+ sc.ChangeState(SCE_CSOUND_HEADERSTMT);
+ } else if (otherKeyword.InList(s)) {
+ sc.ChangeState(SCE_CSOUND_USERKEYWORD);
+ } else if (s[0] == 'p') {
+ sc.ChangeState(SCE_CSOUND_PARAM);
+ } else if (s[0] == 'a') {
+ sc.ChangeState(SCE_CSOUND_ARATE_VAR);
+ } else if (s[0] == 'k') {
+ sc.ChangeState(SCE_CSOUND_KRATE_VAR);
+ } else if (s[0] == 'i') { // covers both i-rate variables and i-statements
+ sc.ChangeState(SCE_CSOUND_IRATE_VAR);
+ } else if (s[0] == 'g') {
+ sc.ChangeState(SCE_CSOUND_GLOBAL_VAR);
+ }
+ sc.SetState(SCE_CSOUND_DEFAULT);
+ }
+ }
+ else if (sc.state == SCE_CSOUND_COMMENT ) {
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_CSOUND_DEFAULT);
+ }
+ }
+ else if ((sc.state == SCE_CSOUND_ARATE_VAR) ||
+ (sc.state == SCE_CSOUND_KRATE_VAR) ||
+ (sc.state == SCE_CSOUND_IRATE_VAR)) {
+ if (!IsAWordChar(sc.ch)) {
+ sc.SetState(SCE_CSOUND_DEFAULT);
+ }
+ }
+
+ // Determine if a new state should be entered.
+ if (sc.state == SCE_CSOUND_DEFAULT) {
+ if (sc.ch == ';'){
+ sc.SetState(SCE_CSOUND_COMMENT);
+ } else if (isdigit(sc.ch) || (sc.ch == '.' && isdigit(sc.chNext))) {
+ sc.SetState(SCE_CSOUND_NUMBER);
+ } else if (IsAWordStart(sc.ch)) {
+ sc.SetState(SCE_CSOUND_IDENTIFIER);
+ } else if (IsCsoundOperator(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_CSOUND_OPERATOR);
+ } else if (sc.ch == 'p') {
+ sc.SetState(SCE_CSOUND_PARAM);
+ } else if (sc.ch == 'a') {
+ sc.SetState(SCE_CSOUND_ARATE_VAR);
+ } else if (sc.ch == 'k') {
+ sc.SetState(SCE_CSOUND_KRATE_VAR);
+ } else if (sc.ch == 'i') { // covers both i-rate variables and i-statements
+ sc.SetState(SCE_CSOUND_IRATE_VAR);
+ } else if (sc.ch == 'g') {
+ sc.SetState(SCE_CSOUND_GLOBAL_VAR);
+ }
+ }
+ }
+ sc.Complete();
+}
+
+static void FoldCsoundInstruments(unsigned int startPos, int length, int /* initStyle */, WordList *[],
+ Accessor &styler) {
+ unsigned int lengthDoc = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
+ int levelCurrent = levelPrev;
+ char chNext = styler[startPos];
+ int stylePrev = 0;
+ int styleNext = styler.StyleAt(startPos);
+ for (unsigned int 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 ((stylePrev != SCE_CSOUND_OPCODE) && (style == SCE_CSOUND_OPCODE)) {
+ char s[20];
+ unsigned int j = 0;
+ while ((j < (sizeof(s) - 1)) && (iswordchar(styler[i + j]))) {
+ s[j] = styler[i + j];
+ j++;
+ }
+ s[j] = '\0';
+
+ if (strcmp(s, "instr") == 0)
+ levelCurrent++;
+ if (strcmp(s, "endin") == 0)
+ 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++;
+ stylePrev = style;
+ }
+ // 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 csoundWordListDesc[] = {
+ "Opcodes",
+ "Header Statements",
+ "User keywords",
+ 0
+};
+
+LexerModule lmCsound(SCLEX_CSOUND, ColouriseCsoundDoc, "csound", FoldCsoundInstruments, csoundWordListDesc);
diff --git a/src/LexEScript.cpp b/src/LexEScript.cpp
new file mode 100755
index 0000000..4941158
--- /dev/null
+++ b/src/LexEScript.cpp
@@ -0,0 +1,270 @@
+// Scintilla source code edit control
+/** @file LexESCRIPT.cxx
+ ** Lexer for ESCRIPT
+ **/
+// Copyright 2003 by Patrizio Bekerle (patrizio@bekerle.com)
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+
+
+static inline bool IsAWordChar(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_');
+}
+
+static inline bool IsAWordStart(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '_');
+}
+
+
+
+static void ColouriseESCRIPTDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+ Accessor &styler) {
+
+ WordList &keywords = *keywordlists[0];
+ WordList &keywords2 = *keywordlists[1];
+ WordList &keywords3 = *keywordlists[2];
+
+ // Do not leak onto next line
+ /*if (initStyle == SCE_ESCRIPT_STRINGEOL)
+ initStyle = SCE_ESCRIPT_DEFAULT;*/
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ bool caseSensitive = styler.GetPropertyInt("escript.case.sensitive", 0) != 0;
+
+ for (; sc.More(); sc.Forward()) {
+
+ /*if (sc.atLineStart && (sc.state == SCE_ESCRIPT_STRING)) {
+ // Prevent SCE_ESCRIPT_STRINGEOL from leaking back to previous line
+ sc.SetState(SCE_ESCRIPT_STRING);
+ }*/
+
+ // Handle line continuation generically.
+ if (sc.ch == '\\') {
+ if (sc.chNext == '\n' || sc.chNext == '\r') {
+ sc.Forward();
+ if (sc.ch == '\r' && sc.chNext == '\n') {
+ sc.Forward();
+ }
+ continue;
+ }
+ }
+
+ // Determine if the current state should terminate.
+ if (sc.state == SCE_ESCRIPT_OPERATOR || sc.state == SCE_ESCRIPT_BRACE) {
+ sc.SetState(SCE_ESCRIPT_DEFAULT);
+ } else if (sc.state == SCE_ESCRIPT_NUMBER) {
+ if (!IsADigit(sc.ch) || sc.ch != '.') {
+ sc.SetState(SCE_ESCRIPT_DEFAULT);
+ }
+ } else if (sc.state == SCE_ESCRIPT_IDENTIFIER) {
+ if (!IsAWordChar(sc.ch) || (sc.ch == '.')) {
+ char s[100];
+ if (caseSensitive) {
+ sc.GetCurrent(s, sizeof(s));
+ } else {
+ sc.GetCurrentLowered(s, sizeof(s));
+ }
+
+// sc.GetCurrentLowered(s, sizeof(s));
+
+ if (keywords.InList(s)) {
+ sc.ChangeState(SCE_ESCRIPT_WORD);
+ } else if (keywords2.InList(s)) {
+ sc.ChangeState(SCE_ESCRIPT_WORD2);
+ } else if (keywords3.InList(s)) {
+ sc.ChangeState(SCE_ESCRIPT_WORD3);
+ // sc.state = SCE_ESCRIPT_IDENTIFIER;
+ }
+ sc.SetState(SCE_ESCRIPT_DEFAULT);
+ }
+ } else if (sc.state == SCE_ESCRIPT_COMMENT) {
+ if (sc.Match('*', '/')) {
+ sc.Forward();
+ sc.ForwardSetState(SCE_ESCRIPT_DEFAULT);
+ }
+ } else if (sc.state == SCE_ESCRIPT_COMMENTDOC) {
+ if (sc.Match('*', '/')) {
+ sc.Forward();
+ sc.ForwardSetState(SCE_ESCRIPT_DEFAULT);
+ }
+ } else if (sc.state == SCE_ESCRIPT_COMMENTLINE) {
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_ESCRIPT_DEFAULT);
+ }
+ } else if (sc.state == SCE_ESCRIPT_STRING) {
+ if (sc.ch == '\\') {
+ if (sc.chNext == '\"' || sc.chNext == '\\') {
+ sc.Forward();
+ }
+ } else if (sc.ch == '\"') {
+ sc.ForwardSetState(SCE_ESCRIPT_DEFAULT);
+ }
+ }
+
+ // Determine if a new state should be entered.
+ if (sc.state == SCE_ESCRIPT_DEFAULT) {
+ if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
+ sc.SetState(SCE_ESCRIPT_NUMBER);
+ } else if (IsAWordStart(sc.ch) || (sc.ch == '#')) {
+ sc.SetState(SCE_ESCRIPT_IDENTIFIER);
+ } else if (sc.Match('/', '*')) {
+ sc.SetState(SCE_ESCRIPT_COMMENT);
+ sc.Forward(); // Eat the * so it isn't used for the end of the comment
+ } else if (sc.Match('/', '/')) {
+ sc.SetState(SCE_ESCRIPT_COMMENTLINE);
+ } else if (sc.ch == '\"') {
+ sc.SetState(SCE_ESCRIPT_STRING);
+ //} else if (isoperator(static_cast<char>(sc.ch))) {
+ } else if (sc.ch == '+' || sc.ch == '-' || sc.ch == '*' || sc.ch == '/' || sc.ch == '=' || sc.ch == '<' || sc.ch == '>' || sc.ch == '&' || sc.ch == '|' || sc.ch == '!' || sc.ch == '?' || sc.ch == ':') {
+ sc.SetState(SCE_ESCRIPT_OPERATOR);
+ } else if (sc.ch == '{' || sc.ch == '}') {
+ sc.SetState(SCE_ESCRIPT_BRACE);
+ }
+ }
+
+ }
+ sc.Complete();
+}
+
+
+static int classifyFoldPointESCRIPT(const char* s, const char* prevWord) {
+ int lev = 0;
+ if (strcmp(prevWord, "end") == 0) return lev;
+ if ((strcmp(prevWord, "else") == 0 && strcmp(s, "if") == 0) || strcmp(s, "elseif") == 0)
+ return -1;
+
+ if (strcmp(s, "for") == 0 || strcmp(s, "foreach") == 0
+ || strcmp(s, "program") == 0 || strcmp(s, "function") == 0
+ || strcmp(s, "while") == 0 || strcmp(s, "case") == 0
+ || strcmp(s, "if") == 0 ) {
+ lev = 1;
+ } else if ( strcmp(s, "endfor") == 0 || strcmp(s, "endforeach") == 0
+ || strcmp(s, "endprogram") == 0 || strcmp(s, "endfunction") == 0
+ || strcmp(s, "endwhile") == 0 || strcmp(s, "endcase") == 0
+ || strcmp(s, "endif") == 0 ) {
+ lev = -1;
+ }
+
+ return lev;
+}
+
+
+static bool IsStreamCommentStyle(int style) {
+ return style == SCE_ESCRIPT_COMMENT ||
+ style == SCE_ESCRIPT_COMMENTDOC ||
+ style == SCE_ESCRIPT_COMMENTLINE;
+}
+
+static void FoldESCRIPTDoc(unsigned int startPos, int length, int initStyle, WordList *[], Accessor &styler) {
+ //~ bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
+ // Do not know how to fold the comment at the moment.
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ bool foldComment = true;
+ unsigned int endPos = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
+ int levelCurrent = levelPrev;
+ char chNext = styler[startPos];
+ int styleNext = styler.StyleAt(startPos);
+ int style = initStyle;
+
+ int lastStart = 0;
+ char prevWord[32] = "";
+
+ for (unsigned int i = startPos; i < endPos; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ int stylePrev = style;
+ style = styleNext;
+ styleNext = styler.StyleAt(i + 1);
+ bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
+
+
+ if (foldComment && IsStreamCommentStyle(style)) {
+ if (!IsStreamCommentStyle(stylePrev)) {
+ levelCurrent++;
+ } else if (!IsStreamCommentStyle(styleNext) && !atEOL) {
+ // Comments don't end at end of line and the next character may be unstyled.
+ levelCurrent--;
+ }
+ }
+
+ if (foldComment && (style == SCE_ESCRIPT_COMMENTLINE)) {
+ if ((ch == '/') && (chNext == '/')) {
+ char chNext2 = styler.SafeGetCharAt(i + 2);
+ if (chNext2 == '{') {
+ levelCurrent++;
+ } else if (chNext2 == '}') {
+ levelCurrent--;
+ }
+ }
+ }
+
+ if (stylePrev == SCE_ESCRIPT_DEFAULT && style == SCE_ESCRIPT_WORD3)
+ {
+ // Store last word start point.
+ lastStart = i;
+ }
+
+ if (style == SCE_ESCRIPT_WORD3) {
+ if(iswordchar(ch) && !iswordchar(chNext)) {
+ char s[32];
+ unsigned int j;
+ for(j = 0; ( j < 31 ) && ( j < i-lastStart+1 ); j++) {
+ s[j] = static_cast<char>(tolower(styler[lastStart + j]));
+ }
+ s[j] = '\0';
+ levelCurrent += classifyFoldPointESCRIPT(s, prevWord);
+ strcpy(prevWord, s);
+ }
+ }
+ if (atEOL) {
+ int lev = levelPrev;
+ if (visibleChars == 0 && foldCompact)
+ 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;
+ strcpy(prevWord, "");
+ }
+
+ 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 ESCRIPTWordLists[] = {
+ "Primary keywords and identifiers",
+ "Intrinsic functions",
+ "Extended and user defined functions",
+ 0,
+};
+
+LexerModule lmESCRIPT(SCLEX_ESCRIPT, ColouriseESCRIPTDoc, "escript", FoldESCRIPTDoc, ESCRIPTWordLists);
diff --git a/src/LexEiffel.cpp b/src/LexEiffel.cpp
new file mode 100755
index 0000000..4aed7c6
--- /dev/null
+++ b/src/LexEiffel.cpp
@@ -0,0 +1,234 @@
+// Scintilla source code edit control
+/** @file LexEiffel.cxx
+ ** Lexer for Eiffel.
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+static inline bool isEiffelOperator(unsigned int ch) {
+ // '.' left out as it is used to make up numbers
+ return ch == '*' || ch == '/' || ch == '\\' || ch == '-' || ch == '+' ||
+ ch == '(' || ch == ')' || ch == '=' ||
+ ch == '{' || ch == '}' || ch == '~' ||
+ ch == '[' || ch == ']' || ch == ';' ||
+ ch == '<' || ch == '>' || ch == ',' ||
+ ch == '.' || ch == '^' || ch == '%' || ch == ':' ||
+ ch == '!' || ch == '@' || ch == '?';
+}
+
+static inline bool IsAWordChar(unsigned int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '_');
+}
+
+static inline bool IsAWordStart(unsigned int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '_');
+}
+
+static void ColouriseEiffelDoc(unsigned int startPos,
+ int length,
+ int initStyle,
+ WordList *keywordlists[],
+ Accessor &styler) {
+
+ WordList &keywords = *keywordlists[0];
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ for (; sc.More(); sc.Forward()) {
+
+ if (sc.state == SCE_EIFFEL_STRINGEOL) {
+ if (sc.ch != '\r' && sc.ch != '\n') {
+ sc.SetState(SCE_EIFFEL_DEFAULT);
+ }
+ } else if (sc.state == SCE_EIFFEL_OPERATOR) {
+ sc.SetState(SCE_EIFFEL_DEFAULT);
+ } else if (sc.state == SCE_EIFFEL_WORD) {
+ if (!IsAWordChar(sc.ch)) {
+ char s[100];
+ sc.GetCurrentLowered(s, sizeof(s));
+ if (!keywords.InList(s)) {
+ sc.ChangeState(SCE_EIFFEL_IDENTIFIER);
+ }
+ sc.SetState(SCE_EIFFEL_DEFAULT);
+ }
+ } else if (sc.state == SCE_EIFFEL_NUMBER) {
+ if (!IsAWordChar(sc.ch)) {
+ sc.SetState(SCE_EIFFEL_DEFAULT);
+ }
+ } else if (sc.state == SCE_EIFFEL_COMMENTLINE) {
+ if (sc.ch == '\r' || sc.ch == '\n') {
+ sc.SetState(SCE_EIFFEL_DEFAULT);
+ }
+ } else if (sc.state == SCE_EIFFEL_STRING) {
+ if (sc.ch == '%') {
+ sc.Forward();
+ } else if (sc.ch == '\"') {
+ sc.Forward();
+ sc.SetState(SCE_EIFFEL_DEFAULT);
+ }
+ } else if (sc.state == SCE_EIFFEL_CHARACTER) {
+ if (sc.ch == '\r' || sc.ch == '\n') {
+ sc.SetState(SCE_EIFFEL_STRINGEOL);
+ } else if (sc.ch == '%') {
+ sc.Forward();
+ } else if (sc.ch == '\'') {
+ sc.Forward();
+ sc.SetState(SCE_EIFFEL_DEFAULT);
+ }
+ }
+
+ if (sc.state == SCE_EIFFEL_DEFAULT) {
+ if (sc.ch == '-' && sc.chNext == '-') {
+ sc.SetState(SCE_EIFFEL_COMMENTLINE);
+ } else if (sc.ch == '\"') {
+ sc.SetState(SCE_EIFFEL_STRING);
+ } else if (sc.ch == '\'') {
+ sc.SetState(SCE_EIFFEL_CHARACTER);
+ } else if (IsADigit(sc.ch) || (sc.ch == '.')) {
+ sc.SetState(SCE_EIFFEL_NUMBER);
+ } else if (IsAWordStart(sc.ch)) {
+ sc.SetState(SCE_EIFFEL_WORD);
+ } else if (isEiffelOperator(sc.ch)) {
+ sc.SetState(SCE_EIFFEL_OPERATOR);
+ }
+ }
+ }
+ sc.Complete();
+}
+
+static bool IsEiffelComment(Accessor &styler, int pos, int len) {
+ return len>1 && styler[pos]=='-' && styler[pos+1]=='-';
+}
+
+static void FoldEiffelDocIndent(unsigned int startPos, int length, int,
+ WordList *[], Accessor &styler) {
+ int lengthDoc = startPos + length;
+
+ // Backtrack to previous line in case need to fix its fold status
+ int lineCurrent = styler.GetLine(startPos);
+ if (startPos > 0) {
+ if (lineCurrent > 0) {
+ lineCurrent--;
+ startPos = styler.LineStart(lineCurrent);
+ }
+ }
+ int spaceFlags = 0;
+ int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, IsEiffelComment);
+ char chNext = styler[startPos];
+ for (int i = startPos; i < lengthDoc; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+
+ if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == lengthDoc)) {
+ int lev = indentCurrent;
+ int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags, IsEiffelComment);
+ if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
+ // Only non whitespace lines can be headers
+ if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) {
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ } else if (indentNext & SC_FOLDLEVELWHITEFLAG) {
+ // Line after is blank so check the next - maybe should continue further?
+ int spaceFlags2 = 0;
+ int indentNext2 = styler.IndentAmount(lineCurrent + 2, &spaceFlags2, IsEiffelComment);
+ if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext2 & SC_FOLDLEVELNUMBERMASK)) {
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ }
+ }
+ }
+ indentCurrent = indentNext;
+ styler.SetLevel(lineCurrent, lev);
+ lineCurrent++;
+ }
+ }
+}
+
+static void FoldEiffelDocKeyWords(unsigned int startPos, int length, int /* initStyle */, WordList *[],
+ Accessor &styler) {
+ unsigned int lengthDoc = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
+ int levelCurrent = levelPrev;
+ char chNext = styler[startPos];
+ int stylePrev = 0;
+ int styleNext = styler.StyleAt(startPos);
+ // lastDeferred should be determined by looking back to last keyword in case
+ // the "deferred" is on a line before "class"
+ bool lastDeferred = false;
+ for (unsigned int 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 ((stylePrev != SCE_EIFFEL_WORD) && (style == SCE_EIFFEL_WORD)) {
+ char s[20];
+ unsigned int j = 0;
+ while ((j < (sizeof(s) - 1)) && (iswordchar(styler[i + j]))) {
+ s[j] = styler[i + j];
+ j++;
+ }
+ s[j] = '\0';
+
+ if (
+ (strcmp(s, "check") == 0) ||
+ (strcmp(s, "debug") == 0) ||
+ (strcmp(s, "deferred") == 0) ||
+ (strcmp(s, "do") == 0) ||
+ (strcmp(s, "from") == 0) ||
+ (strcmp(s, "if") == 0) ||
+ (strcmp(s, "inspect") == 0) ||
+ (strcmp(s, "once") == 0)
+ )
+ levelCurrent++;
+ if (!lastDeferred && (strcmp(s, "class") == 0))
+ levelCurrent++;
+ if (strcmp(s, "end") == 0)
+ levelCurrent--;
+ lastDeferred = strcmp(s, "deferred") == 0;
+ }
+
+ 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++;
+ stylePrev = style;
+ }
+ // 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 eiffelWordListDesc[] = {
+ "Keywords",
+ 0
+};
+
+LexerModule lmEiffel(SCLEX_EIFFEL, ColouriseEiffelDoc, "eiffel", FoldEiffelDocIndent, eiffelWordListDesc);
+LexerModule lmEiffelkw(SCLEX_EIFFELKW, ColouriseEiffelDoc, "eiffelkw", FoldEiffelDocKeyWords, eiffelWordListDesc);
diff --git a/src/LexErlang.cpp b/src/LexErlang.cpp
new file mode 100755
index 0000000..9444eb9
--- /dev/null
+++ b/src/LexErlang.cpp
@@ -0,0 +1,522 @@
+// Scintilla source code edit control
+/** @file LexErlang.cxx
+ ** Lexer for Erlang.
+ ** Written by Peter-Henry Mander, based on Matlab lexer by José Fonseca
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+/*
+ TODO:
+ o _Param should be a new lexical type
+*/
+
+static int is_radix(int radix, int ch) {
+ int digit;
+ if ( 16 < radix || 2 > radix ) {
+ return 0;
+ }
+ if ( isdigit(ch) ) {
+ digit = ch - '0';
+ } else if ( isxdigit(ch) ) {
+ digit = toupper(ch) - 'A' + 10;
+ } else {
+ return 0;
+ }
+ if ( digit < radix ) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+typedef enum {
+ STATE_NULL,
+ ATOM_UNQUOTED,
+ ATOM_QUOTED,
+ ATOM_FUN_NAME,
+ NODE_NAME_UNQUOTED,
+ NODE_NAME_QUOTED,
+ MACRO_START,
+ MACRO_UNQUOTED,
+ MACRO_QUOTED,
+ RECORD_START,
+ RECORD_UNQUOTED,
+ RECORD_QUOTED,
+ NUMERAL_START,
+ NUMERAL_SIGNED,
+ NUMERAL_RADIX_LITERAL,
+ NUMERAL_SPECULATIVE_MANTISSA,
+ NUMERAL_FLOAT_MANTISSA,
+ NUMERAL_FLOAT_EXPONENT,
+ NUMERAL_FLOAT_SIGNED_EXPONENT,
+ PARSE_ERROR
+} atom_parse_state_t;
+
+static void ColouriseErlangDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler) {
+
+ WordList &keywords = *keywordlists[0];
+
+ styler.StartAt(startPos);
+
+ StyleContext sc(startPos, length, initStyle, styler);
+ atom_parse_state_t parse_state = STATE_NULL;
+ int radix_digits = 0;
+ int exponent_digits = 0;
+ for (; sc.More(); sc.Forward()) {
+ if ( STATE_NULL != parse_state ) {
+ switch (parse_state) {
+ case STATE_NULL:
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ break;
+ case ATOM_UNQUOTED:
+ if ( '@' == sc.ch ){
+ parse_state = NODE_NAME_UNQUOTED;
+ } else if ( !isalnum(sc.ch) && sc.ch != '_' ) {
+ char s[100];
+ sc.GetCurrent(s, sizeof(s));
+ if (keywords.InList(s)) {
+ sc.ChangeState(SCE_ERLANG_KEYWORD);
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ } else {
+ if ( '/' == sc.ch ) {
+ parse_state = ATOM_FUN_NAME;
+ } else {
+ sc.ChangeState(SCE_ERLANG_ATOM);
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ }
+ }
+ }
+ break;
+ case ATOM_QUOTED:
+ if ( '@' == sc.ch ){
+ parse_state = NODE_NAME_QUOTED;
+ } else if ( '\'' == sc.ch && '\\' != sc.chPrev ) {
+ sc.ChangeState(SCE_ERLANG_ATOM);
+ sc.ForwardSetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ }
+ break;
+ case ATOM_FUN_NAME:
+ if ( !isdigit(sc.ch) ) {
+ sc.ChangeState(SCE_ERLANG_FUNCTION_NAME);
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ }
+ break;
+ case NODE_NAME_QUOTED:
+ if ( '@' == sc.ch ) {
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ } else if ( '\'' == sc.ch && '\\' != sc.chPrev ) {
+ sc.ChangeState(SCE_ERLANG_NODE_NAME);
+ sc.ForwardSetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ }
+ break;
+ case NODE_NAME_UNQUOTED:
+ if ( '@' == sc.ch ) {
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ } else if ( !isalnum(sc.ch) && sc.ch != '_' ) {
+ sc.ChangeState(SCE_ERLANG_NODE_NAME);
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ }
+ break;
+ case RECORD_START:
+ if ( '\'' == sc.ch ) {
+ parse_state = RECORD_QUOTED;
+ } else if (isalpha(sc.ch) && islower(sc.ch)) {
+ parse_state = RECORD_UNQUOTED;
+ } else { // error
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ }
+ break;
+ case RECORD_QUOTED:
+ if ( '\'' == sc.ch && '\\' != sc.chPrev ) {
+ sc.ChangeState(SCE_ERLANG_RECORD);
+ sc.ForwardSetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ }
+ break;
+ case RECORD_UNQUOTED:
+ if ( !isalpha(sc.ch) && '_' != sc.ch ) {
+ sc.ChangeState(SCE_ERLANG_RECORD);
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ }
+ break;
+ case MACRO_START:
+ if ( '\'' == sc.ch ) {
+ parse_state = MACRO_QUOTED;
+ } else if (isalpha(sc.ch)) {
+ parse_state = MACRO_UNQUOTED;
+ } else { // error
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ }
+ break;
+ case MACRO_UNQUOTED:
+ if ( !isalpha(sc.ch) && '_' != sc.ch ) {
+ sc.ChangeState(SCE_ERLANG_MACRO);
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ }
+ break;
+ case MACRO_QUOTED:
+ if ( '\'' == sc.ch && '\\' != sc.chPrev ) {
+ sc.ChangeState(SCE_ERLANG_MACRO);
+ sc.ForwardSetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ }
+ break;
+ case NUMERAL_START:
+ if ( isdigit(sc.ch) ) {
+ radix_digits *= 10;
+ radix_digits += sc.ch - '0'; // Assuming ASCII here!
+ } else if ( '#' == sc.ch ) {
+ if ( 2 > radix_digits || 16 < radix_digits) {
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ } else {
+ parse_state = NUMERAL_RADIX_LITERAL;
+ }
+ } else if ( '.' == sc.ch && isdigit(sc.chNext)) {
+ radix_digits = 0;
+ parse_state = NUMERAL_FLOAT_MANTISSA;
+ } else if ( 'e' == sc.ch || 'E' == sc.ch ) {
+ exponent_digits = 0;
+ parse_state = NUMERAL_FLOAT_EXPONENT;
+ } else {
+ radix_digits = 0;
+ sc.ChangeState(SCE_ERLANG_NUMBER);
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ }
+ break;
+ case NUMERAL_RADIX_LITERAL:
+ if ( !is_radix(radix_digits,sc.ch) ) {
+ radix_digits = 0;
+ if ( !isalnum(sc.ch) ) {
+ sc.ChangeState(SCE_ERLANG_NUMBER);
+ }
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ }
+ break;
+ case NUMERAL_FLOAT_MANTISSA:
+ if ( 'e' == sc.ch || 'E' == sc.ch ) {
+ exponent_digits = 0;
+ parse_state = NUMERAL_FLOAT_EXPONENT;
+ } else if ( !isdigit(sc.ch) ) {
+ sc.ChangeState(SCE_ERLANG_NUMBER);
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ }
+ break;
+ case NUMERAL_FLOAT_EXPONENT:
+ if ( '-' == sc.ch || '+' == sc.ch ) {
+ parse_state = NUMERAL_FLOAT_SIGNED_EXPONENT;
+ } else if ( !isdigit(sc.ch) ) {
+ if ( 0 < exponent_digits ) {
+ sc.ChangeState(SCE_ERLANG_NUMBER);
+ }
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ } else {
+ ++exponent_digits;
+ }
+ break;
+ case NUMERAL_FLOAT_SIGNED_EXPONENT:
+ if ( !isdigit(sc.ch) ) {
+ if ( 0 < exponent_digits ) {
+ sc.ChangeState(SCE_ERLANG_NUMBER);
+ }
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ } else {
+ ++exponent_digits;
+ }
+ break;
+ case NUMERAL_SIGNED:
+ if ( !isdigit(sc.ch) ) {
+ sc.ChangeState(SCE_ERLANG_NUMBER);
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ } else if ( '.' == sc.ch ) {
+ parse_state = NUMERAL_FLOAT_MANTISSA;
+ }
+ break;
+ case NUMERAL_SPECULATIVE_MANTISSA:
+ if ( !isdigit(sc.ch) ) {
+ sc.ChangeState(SCE_ERLANG_OPERATOR);
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ } else {
+ parse_state = NUMERAL_FLOAT_MANTISSA;
+ }
+ break;
+ case PARSE_ERROR:
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ parse_state = STATE_NULL;
+ break;
+ }
+ } else if (sc.state == SCE_ERLANG_OPERATOR) {
+ if (sc.chPrev == '.') {
+ if (sc.ch == '*' || sc.ch == '/' || sc.ch == '\\' || sc.ch == '^') {
+ sc.ForwardSetState(SCE_ERLANG_DEFAULT);
+ } else if (sc.ch == '\'') {
+ sc.ForwardSetState(SCE_ERLANG_DEFAULT);
+ } else {
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ }
+ } else {
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ }
+ } else if (sc.state == SCE_ERLANG_VARIABLE) {
+ if (!isalnum(sc.ch) && sc.ch != '_') {
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ }
+ } else if (sc.state == SCE_ERLANG_STRING) {
+ if (sc.ch == '\"' && sc.chPrev != '\\') {
+ sc.ForwardSetState(SCE_ERLANG_DEFAULT);
+ }
+ } else if (sc.state == SCE_ERLANG_COMMENT ) {
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_ERLANG_DEFAULT);
+ }
+ } else if (sc.state == SCE_ERLANG_CHARACTER ) {
+ if ( sc.chPrev == '\\' ) {
+ sc.ForwardSetState(SCE_ERLANG_DEFAULT);
+ } else if ( sc.ch != '\\' ) {
+ sc.ForwardSetState(SCE_ERLANG_DEFAULT);
+ }
+ }
+
+ if (sc.state == SCE_ERLANG_DEFAULT) {
+ if (sc.ch == '%') {
+ sc.SetState(SCE_ERLANG_COMMENT);
+ } else if (sc.ch == '\"') {
+ sc.SetState(SCE_ERLANG_STRING);
+ } else if (sc.ch == '#') {
+ parse_state = RECORD_START;
+ sc.SetState(SCE_ERLANG_UNKNOWN);
+ } else if (sc.ch == '?') {
+ parse_state = MACRO_START;
+ sc.SetState(SCE_ERLANG_UNKNOWN);
+ } else if (sc.ch == '$') {
+ sc.SetState(SCE_ERLANG_CHARACTER);
+ } else if (sc.ch == '\'') {
+ parse_state = ATOM_QUOTED;
+ sc.SetState(SCE_ERLANG_UNKNOWN);
+ } else if ( isdigit(sc.ch) ) {
+ parse_state = NUMERAL_START;
+ radix_digits = sc.ch - '0';
+ sc.SetState(SCE_ERLANG_UNKNOWN);
+ } else if ( '.' == sc.ch ) {
+ parse_state = NUMERAL_SPECULATIVE_MANTISSA;
+ sc.SetState(SCE_ERLANG_UNKNOWN);
+ } else if (isalpha(sc.ch) && isupper(sc.ch)) {
+ sc.SetState(SCE_ERLANG_VARIABLE);
+ } else if (isalpha(sc.ch)) {
+ parse_state = ATOM_UNQUOTED;
+ sc.SetState(SCE_ERLANG_UNKNOWN);
+ } else if (isoperator(static_cast<char>(sc.ch)) || sc.ch == '\\') {
+ sc.SetState(SCE_ERLANG_OPERATOR);
+ }
+ }
+ }
+ sc.Complete();
+}
+
+static int ClassifyFoldPointErlang(
+ Accessor &styler,
+ int styleNext,
+ int keyword_start
+) {
+ int lev = 0;
+ if ( styler.Match(keyword_start,"case")
+ || (
+ styler.Match(keyword_start,"fun")
+ && SCE_ERLANG_FUNCTION_NAME != styleNext)
+ || styler.Match(keyword_start,"if")
+ || styler.Match(keyword_start,"query")
+ || styler.Match(keyword_start,"receive")
+ ) {
+ ++lev;
+ } else if ( styler.Match(keyword_start,"end") ) {
+ --lev;
+ }
+ return lev;
+}
+
+
+static void FoldErlangDoc(
+ unsigned int startPos, int length, int initStyle,
+ WordList** /*keywordlists*/, Accessor &styler
+) {
+ unsigned int endPos = startPos + length;
+ //~ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
+ int levelCurrent = levelPrev;
+ char chNext = styler.SafeGetCharAt(startPos);
+ int styleNext = styler.StyleAt(startPos);
+ int style = initStyle;
+ int keyword_start = 0;
+
+ bool fold_keywords = true;
+ bool fold_comments = true;
+ bool fold_braces = true;
+ bool fold_function_clauses = false;
+ bool fold_clauses = false;
+
+ //int clause_level = 0;
+
+ for (unsigned int i = startPos; i < endPos; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ int stylePrev = style;
+ style = styleNext;
+ styleNext = styler.StyleAt(i + 1);
+ bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
+
+ if ( (stylePrev != SCE_ERLANG_KEYWORD) && (style == SCE_ERLANG_KEYWORD) ) {
+ keyword_start = i;
+ }
+ if ( fold_keywords ) {
+ if ( (stylePrev == SCE_ERLANG_KEYWORD)
+ && (style != SCE_ERLANG_KEYWORD)
+ && (style != SCE_ERLANG_ATOM)
+ ) {
+ levelCurrent += ClassifyFoldPointErlang(styler,styleNext,keyword_start);
+ }
+ }
+
+ if ( fold_comments ) {
+ if (style == SCE_ERLANG_COMMENT) {
+ if ((ch == '%') && (chNext == '{')) {
+ levelCurrent++;
+ } else if ((ch == '%') && (chNext == '}')) {
+ levelCurrent--;
+ }
+ }
+ }
+
+ if ( fold_function_clauses ) {
+ if ( (SC_FOLDLEVELBASE == levelCurrent) /*&& (style == SCE_ERLANG_OPERATOR)*/ ) {
+ if ( (ch == '-') && (chNext == '>')) {
+ //~ fprintf(stderr,"levelCurrent=%d\n", levelCurrent);
+ //++clause_level;
+ //~ if ( 0 < clause_level )
+ ++levelCurrent;
+ }
+ }
+ //~ if ( (stylePrev != SCE_ERLANG_RECORD)
+ //~ && (style != SCE_ERLANG_NUMBER)
+ //~ && (style != SCE_ERLANG_STRING)
+ //~ && (style != SCE_ERLANG_COMMENT)
+ //~ ) {
+ if ( (SC_FOLDLEVELBASE+1 == levelCurrent) && (ch == '.') ) {
+ //--clause_level;
+ //~ if ( 0 == clause_level )
+ --levelCurrent;
+ }
+ //~ }
+ }
+
+ if ( fold_clauses ) {
+ if ( (0 < levelCurrent) && (style == SCE_ERLANG_OPERATOR) ) {
+ if ((ch == '-') && (chNext == '>')) {
+ levelCurrent++;
+ }
+ if ( (ch == ';') ) {
+ levelCurrent--;
+ }
+ }
+ if ( (stylePrev != SCE_ERLANG_RECORD)
+ && (style != SCE_ERLANG_NUMBER)
+ && (style != SCE_ERLANG_STRING)
+ && (style != SCE_ERLANG_COMMENT)
+ ) {
+ if ( (ch == '.') ) {
+ levelCurrent--;
+ }
+ }
+ if ( (stylePrev == SCE_ERLANG_KEYWORD)
+ && (style != SCE_ERLANG_KEYWORD)
+ && (style != SCE_ERLANG_ATOM)
+ && (
+ styler.Match(keyword_start,"end") // 'end' counted twice if fold_keywords too
+ || styler.Match(keyword_start,"after") )
+ ) {
+ levelCurrent--;
+ }
+ }
+
+ if ( fold_braces ) {
+ if (style == SCE_ERLANG_OPERATOR) {
+ if ( (ch == '{') || (ch == '(') || (ch == '[') ) {
+ levelCurrent++;
+ } else if ( (ch == '}') || (ch == ')') || (ch == ']') ) {
+ levelCurrent--;
+ }
+ }
+ }
+
+ if (atEOL) {
+ int lev = levelPrev;
+ //~ if (visibleChars == 0 && foldCompact)
+ //~ lev |= SC_FOLDLEVELWHITEFLAG;
+ //~ if ((levelCurrent > levelPrev) && (visibleChars > 0))
+ if ((levelCurrent > levelPrev)) {
+ 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 erlangWordListDesc[] = {
+ "Keywords",
+ 0
+};
+
+LexerModule lmErlang(
+ SCLEX_ERLANG,
+ ColouriseErlangDoc,
+ "erlang",
+ FoldErlangDoc,
+ erlangWordListDesc);
+
diff --git a/src/LexFlagship.cpp b/src/LexFlagship.cpp
new file mode 100644
index 0000000..db0314e
--- /dev/null
+++ b/src/LexFlagship.cpp
@@ -0,0 +1,226 @@
+// Scintilla source code edit control
+/** @file LexFlagShip.cxx
+ ** Lexer for FlagShip
+ ** (Syntactically compatible to other XBase dialects, like dBase, Clipper, Fox etc.)
+ **/
+// Copyright 2005 by Randy Butler
+// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+static bool IsFlagShipComment(Accessor &styler, int pos, int len) {
+ return len>0 && styler[pos]=='\'';
+}
+
+static inline bool IsTypeCharacter(int ch) {
+ return ch == '%' || ch == '&' || ch == '@' || ch == '!' || ch == '#' || ch == '$';
+}
+
+// Extended to accept accented characters
+static inline bool IsAWordChar(int ch) {
+ return ch >= 0x80 ||
+ (isalnum(ch) || ch == '.' || ch == '_');
+}
+
+static inline bool IsAWordStart(int ch) {
+ return ch >= 0x80 ||
+ (isalnum(ch) || ch == '_');
+}
+
+static inline bool IsADateCharacter(const int ch) {
+ return (ch < 0x80) &&
+ (isalnum(ch) || ch == '|' || ch == '-' || ch == '/' || ch == ':' || ch == ' ' || ch == '\t');
+}
+
+
+static void ColouriseFlagShipDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler) {
+
+ //bool FSScriptSyntax = true;
+ WordList &keywords = *keywordlists[0];
+ WordList &keywords2 = *keywordlists[1];
+ WordList &keywords3 = *keywordlists[2];
+ WordList &keywords4 = *keywordlists[3];
+
+ styler.StartAt(startPos);
+
+ int visibleChars = 0;
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ for (; sc.More(); sc.Forward()) {
+
+ if (sc.state == SCE_FS_OPERATOR) {
+ sc.SetState(SCE_FS_DEFAULT);
+ } else if (sc.state == SCE_FS_IDENTIFIER) {
+ if (!IsAWordChar(sc.ch)) {
+ char s[100];
+ sc.GetCurrentLowered(s, sizeof(s));
+ if (keywords.InList(s)) {
+ sc.ChangeState(SCE_FS_KEYWORD);
+ } else if (keywords2.InList(s)) {
+ sc.ChangeState(SCE_FS_KEYWORD2);
+ } else if (keywords3.InList(s)) {
+ sc.ChangeState(SCE_FS_KEYWORD3);
+ } else if (keywords4.InList(s)) {
+ sc.ChangeState(SCE_FS_KEYWORD4);
+ }// Else, it is really an identifier...
+ sc.SetState(SCE_FS_DEFAULT);
+ }
+ } else if (sc.state == SCE_FS_NUMBER) {
+ if (!IsAWordChar(sc.ch)) {
+ sc.SetState(SCE_FS_DEFAULT);
+ }
+ } else if (sc.state == SCE_FS_STRING) {
+ // VB doubles quotes to preserve them, so just end this string
+ // state now as a following quote will start again
+ if (sc.ch == '\"') {
+ if (tolower(sc.chNext) == 'c') {
+ sc.Forward();
+ }
+ sc.ForwardSetState(SCE_FS_DEFAULT);
+ } else if (sc.atLineEnd) {
+ sc.ChangeState(SCE_FS_STRINGEOL);
+ sc.ForwardSetState(SCE_FS_DEFAULT);
+ }
+ } else if (sc.state == SCE_FS_COMMENT) {
+ if (sc.Match('*', '/')) { // new code
+ sc.Forward();
+ sc.ForwardSetState(SCE_FS_DEFAULT);
+ //if (sc.atLineEnd) { // old code
+ // sc.SetState(SCE_FS_DEFAULT);
+ }
+ } else if (sc.state == SCE_FS_COMMENTLINE) { //new code
+ if (sc.ch == '\r' || sc.ch == '\n') {
+ sc.SetState(SCE_FS_DEFAULT);
+ visibleChars = 0;
+ }
+ } else if (sc.state == SCE_FS_PREPROCESSOR) {
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_FS_DEFAULT);
+ }
+ } else if (sc.state == SCE_FS_DATE) {
+ if (sc.ch == '#' || !IsADateCharacter(sc.chNext)) {
+ sc.ForwardSetState(SCE_FS_DEFAULT);
+ }
+ }
+
+ // Determine if a new state should be entered.
+ if (sc.state == SCE_FS_DEFAULT) {
+ if (sc.Match('/', '*')) { // New code
+ sc.SetState(SCE_FS_COMMENT);
+ sc.Forward(); // Eat the * so it isn't used for the end of the comment
+ //if (sc.ch == '\'') { // Old code
+ // sc.SetState(SCE_FS_COMMENT); // old code
+ } else if (sc.Match('/', '/')) { // New code
+ sc.SetState(SCE_FS_COMMENTLINE);
+ } else if (sc.ch == '\"') {
+ sc.SetState(SCE_FS_STRING);
+ } else if (sc.ch == '#' && visibleChars == 0) {
+ // Preprocessor commands are alone on their line
+ sc.SetState(SCE_FS_PREPROCESSOR);
+ } else if (sc.ch == '#') {
+ int n = 1;
+ int chSeek = ' ';
+ while ((n < 100) && (chSeek == ' ' || chSeek == '\t')) {
+ chSeek = sc.GetRelative(n);
+ n++;
+ }
+ if (IsADigit(chSeek)) {
+ sc.SetState(SCE_FS_DATE);
+ } else {
+ sc.SetState(SCE_FS_OPERATOR);
+ }
+ } else if (sc.ch == '&' && tolower(sc.chNext) == 'h') {
+ sc.SetState(SCE_FS_NUMBER);
+ } else if (sc.ch == '&' && tolower(sc.chNext) == 'o') {
+ sc.SetState(SCE_FS_NUMBER);
+ } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
+ sc.SetState(SCE_FS_NUMBER);
+ } else if (IsAWordStart(sc.ch) || (sc.ch == '[')) {
+ sc.SetState(SCE_FS_IDENTIFIER);
+ } else if (isoperator(static_cast<char>(sc.ch)) || (sc.ch == '\\')) {
+ sc.SetState(SCE_FS_OPERATOR);
+ }
+ }
+
+ if (sc.atLineEnd) {
+ visibleChars = 0;
+ }
+ if (!IsASpace(sc.ch)) {
+ visibleChars++;
+ }
+ }
+ sc.Complete();
+}
+
+static void FoldFlagShipDoc(unsigned int startPos, int length, int,
+ WordList *[], Accessor &styler) {
+
+ int endPos = startPos + length;
+
+ // Backtrack to previous line in case need to fix its fold status
+ int lineCurrent = styler.GetLine(startPos);
+ if (startPos > 0) {
+ if (lineCurrent > 0) {
+ lineCurrent--;
+ startPos = styler.LineStart(lineCurrent);
+ }
+ }
+ int spaceFlags = 0;
+ int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, IsFlagShipComment);
+ char chNext = styler[startPos];
+ for (int i = startPos; i < endPos; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+
+ if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == endPos)) {
+ int lev = indentCurrent;
+ int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags, IsFlagShipComment);
+ if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
+ // Only non whitespace lines can be headers
+ if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) {
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ } else if (indentNext & SC_FOLDLEVELWHITEFLAG) {
+ // Line after is blank so check the next - maybe should continue further?
+ int spaceFlags2 = 0;
+ int indentNext2 = styler.IndentAmount(lineCurrent + 2, &spaceFlags2, IsFlagShipComment);
+ if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext2 & SC_FOLDLEVELNUMBERMASK)) {
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ }
+ }
+ }
+ indentCurrent = indentNext;
+ styler.SetLevel(lineCurrent, lev);
+ lineCurrent++;
+ }
+ }
+}
+
+
+static const char * const FSWordListDesc[] = {
+ "Keywords",
+ "functions",
+ "user2",
+ "user3",
+ 0
+};
+
+LexerModule lmFlagShip(SCLEX_FLAGSHIP, ColouriseFlagShipDoc, "flagship", FoldFlagShipDoc, FSWordListDesc);
+
+
+
diff --git a/src/LexForth.cpp b/src/LexForth.cpp
new file mode 100755
index 0000000..3f12815
--- /dev/null
+++ b/src/LexForth.cpp
@@ -0,0 +1,348 @@
+// Scintilla source code edit control
+/** @file LexCrontab.cxx
+ ** Lexer to use with extended crontab files used by a powerful
+ ** Windows scheduler/event monitor/automation manager nnCron.
+ ** (http://nemtsev.eserv.ru/)
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+bool is_whitespace(int ch){
+ return ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ';
+}
+
+bool is_blank(int ch){
+ return ch == '\t' || ch == ' ';
+}
+//#define FORTH_DEBUG
+#ifdef FORTH_DEBUG
+static FILE *f_debug;
+#define log(x) fputs(f_debug,x);
+#else
+#define log(x)
+#endif
+
+#define STATE_LOCALE
+#define BL ' '
+
+static Accessor *st;
+static int cur_pos,pos1,pos2,pos0,lengthDoc;
+char *buffer;
+
+char getChar(bool is_bl){
+ char ch=st->SafeGetCharAt(cur_pos);
+ if(is_bl) if(is_whitespace(ch)) ch=BL;
+ return ch;
+}
+
+char getCharBL(){
+ char ch=st->SafeGetCharAt(cur_pos);
+ return ch;
+}
+bool is_eol(char ch){
+ return ch=='\n' || ch=='\r';
+}
+
+int parse(char ch, bool skip_eol){
+// pos1 - start pos of word
+// pos2 - pos after of word
+// pos0 - start pos
+ char c=0;
+ int len;
+ bool is_bl=ch==BL;
+ pos0=pos1=pos2=cur_pos;
+ for(;cur_pos<lengthDoc && (c=getChar(is_bl))==ch; cur_pos++){
+ if(is_eol(c) && !skip_eol){
+ pos2=pos1;
+ return 0;
+ }
+ }
+ pos1=cur_pos;
+ pos2=pos1;
+ if(cur_pos==lengthDoc) return 0;
+ for(len=0;cur_pos<lengthDoc && (c=getChar(is_bl))!=ch; cur_pos++){
+ if(is_eol(c) && !skip_eol) break;
+ pos2++;
+ buffer[len++]=c;
+ }
+ if(c==ch) pos2--;
+ buffer[len]='\0';
+#ifdef FORTH_DEBUG
+ fprintf(f_debug,"parse: %c %s\n",ch,buffer);
+#endif
+ return len;
+}
+
+bool _is_number(char *s,int base){
+ for(;*s;s++){
+ int digit=((int)*s)-(int)'0';
+#ifdef FORTH_DEBUG
+ fprintf(f_debug,"digit: %c %d\n",*s,digit);
+#endif
+ if(digit>9 && base>10) digit-=7;
+ if(digit<0) return false;
+ if(digit>=base) return false;
+ }
+ return true;
+}
+
+bool is_number(char *s){
+ if(strncmp(s,"0x",2)==0) return _is_number(s+2,16);
+ return _is_number(s,10);
+}
+
+static void ColouriseForthDoc(unsigned int startPos, int length, int, WordList *keywordLists[], Accessor &styler)
+{
+ st=&styler;
+ cur_pos=startPos;
+ lengthDoc = startPos + length;
+ buffer = new char[length];
+
+#ifdef FORTH_DEBUG
+ f_debug=fopen("c:\\sci.log","at");
+#endif
+
+ WordList &control = *keywordLists[0];
+ WordList &keyword = *keywordLists[1];
+ WordList &defword = *keywordLists[2];
+ WordList &preword1 = *keywordLists[3];
+ WordList &preword2 = *keywordLists[4];
+ WordList &strings = *keywordLists[5];
+
+ // go through all provided text segment
+ // using the hand-written state machine shown below
+ styler.StartAt(startPos);
+ styler.StartSegment(startPos);
+ while(parse(BL,true)!=0){
+ if(pos0!=pos1){
+ styler.ColourTo(pos0,SCE_FORTH_DEFAULT);
+ styler.ColourTo(pos1-1,SCE_FORTH_DEFAULT);
+ }
+ if(strcmp("\\",buffer)==0){
+ styler.ColourTo(pos1,SCE_FORTH_COMMENT);
+ parse(1,false);
+ styler.ColourTo(pos2,SCE_FORTH_COMMENT);
+ }else if(strcmp("(",buffer)==0){
+ styler.ColourTo(pos1,SCE_FORTH_COMMENT);
+ parse(')',true);
+ if(cur_pos<lengthDoc) cur_pos++;
+ styler.ColourTo(cur_pos,SCE_FORTH_COMMENT);
+ }else if(strcmp("[",buffer)==0){
+ styler.ColourTo(pos1,SCE_FORTH_STRING);
+ parse(']',true);
+ if(cur_pos<lengthDoc) cur_pos++;
+ styler.ColourTo(cur_pos,SCE_FORTH_STRING);
+ }else if(strcmp("{",buffer)==0){
+ styler.ColourTo(pos1,SCE_FORTH_LOCALE);
+ parse('}',false);
+ if(cur_pos<lengthDoc) cur_pos++;
+ styler.ColourTo(cur_pos,SCE_FORTH_LOCALE);
+ }else if(strings.InList(buffer)) {
+ styler.ColourTo(pos1,SCE_FORTH_STRING);
+ parse('"',false);
+ if(cur_pos<lengthDoc) cur_pos++;
+ styler.ColourTo(cur_pos,SCE_FORTH_STRING);
+ }else if(control.InList(buffer)) {
+ styler.ColourTo(pos1,SCE_FORTH_CONTROL);
+ styler.ColourTo(pos2,SCE_FORTH_CONTROL);
+ }else if(keyword.InList(buffer)) {
+ styler.ColourTo(pos1,SCE_FORTH_KEYWORD);
+ styler.ColourTo(pos2,SCE_FORTH_KEYWORD);
+ }else if(defword.InList(buffer)) {
+ styler.ColourTo(pos1,SCE_FORTH_KEYWORD);
+ styler.ColourTo(pos2,SCE_FORTH_KEYWORD);
+ parse(BL,false);
+ styler.ColourTo(pos1-1,SCE_FORTH_DEFAULT);
+ styler.ColourTo(pos1,SCE_FORTH_DEFWORD);
+ styler.ColourTo(pos2,SCE_FORTH_DEFWORD);
+ }else if(preword1.InList(buffer)) {
+ styler.ColourTo(pos1,SCE_FORTH_PREWORD1);
+ parse(BL,false);
+ styler.ColourTo(pos2,SCE_FORTH_PREWORD1);
+ }else if(preword2.InList(buffer)) {
+ styler.ColourTo(pos1,SCE_FORTH_PREWORD2);
+ parse(BL,false);
+ styler.ColourTo(pos2,SCE_FORTH_PREWORD2);
+ parse(BL,false);
+ styler.ColourTo(pos1,SCE_FORTH_STRING);
+ styler.ColourTo(pos2,SCE_FORTH_STRING);
+ }else if(is_number(buffer)){
+ styler.ColourTo(pos1,SCE_FORTH_NUMBER);
+ styler.ColourTo(pos2,SCE_FORTH_NUMBER);
+ }
+ }
+#ifdef FORTH_DEBUG
+ fclose(f_debug);
+#endif
+ delete []buffer;
+ return;
+/*
+ if(control.InList(buffer)) {
+ styler.ColourTo(i,SCE_FORTH_CONTROL);
+ } else if(keyword.InList(buffer)) {
+ styler.ColourTo(i-1,SCE_FORTH_KEYWORD );
+ } else if(defword.InList(buffer)) {
+ styler.ColourTo(i-1,SCE_FORTH_DEFWORD );
+// prev_state=SCE_FORTH_DEFWORD
+ } else if(preword1.InList(buffer)) {
+ styler.ColourTo(i-1,SCE_FORTH_PREWORD1 );
+// state=SCE_FORTH_PREWORD1;
+ } else if(preword2.InList(buffer)) {
+ styler.ColourTo(i-1,SCE_FORTH_PREWORD2 );
+ } else {
+ styler.ColourTo(i-1,SCE_FORTH_DEFAULT);
+ }
+*/
+/*
+ chPrev=' ';
+ for (int i = startPos; i < lengthDoc; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ if(i!=startPos) chPrev=styler.SafeGetCharAt(i - 1);
+
+ if (styler.IsLeadByte(ch)) {
+ chNext = styler.SafeGetCharAt(i + 2);
+ i++;
+ continue;
+ }
+#ifdef FORTH_DEBUG
+ fprintf(f_debug,"%c %d ",ch,state);
+#endif
+ switch(state) {
+ case SCE_FORTH_DEFAULT:
+ if(is_whitespace(ch)) {
+ // whitespace is simply ignored here...
+ styler.ColourTo(i,SCE_FORTH_DEFAULT);
+ break;
+ } else if( ch == '\\' && is_blank(chNext)) {
+ // signals the start of an one line comment...
+ state = SCE_FORTH_COMMENT;
+ styler.ColourTo(i,SCE_FORTH_COMMENT);
+ } else if( is_whitespace(chPrev) && ch == '(' && is_whitespace(chNext)) {
+ // signals the start of a plain comment...
+ state = SCE_FORTH_COMMENT_ML;
+ styler.ColourTo(i,SCE_FORTH_COMMENT_ML);
+ } else if( isdigit(ch) ) {
+ // signals the start of a number
+ bufferCount = 0;
+ buffer[bufferCount++] = ch;
+ state = SCE_FORTH_NUMBER;
+ } else if( !is_whitespace(ch)) {
+ // signals the start of an identifier
+ bufferCount = 0;
+ buffer[bufferCount++] = ch;
+ state = SCE_FORTH_IDENTIFIER;
+ } else {
+ // style it the default style..
+ styler.ColourTo(i,SCE_FORTH_DEFAULT);
+ }
+ break;
+
+ case SCE_FORTH_COMMENT:
+ // if we find a newline here,
+ // we simply go to default state
+ // else continue to work on it...
+ if( ch == '\n' || ch == '\r' ) {
+ state = SCE_FORTH_DEFAULT;
+ } else {
+ styler.ColourTo(i,SCE_FORTH_COMMENT);
+ }
+ break;
+
+ case SCE_FORTH_COMMENT_ML:
+ if( ch == ')') {
+ state = SCE_FORTH_DEFAULT;
+ } else {
+ styler.ColourTo(i+1,SCE_FORTH_COMMENT_ML);
+ }
+ break;
+
+ case SCE_FORTH_IDENTIFIER:
+ // stay in CONF_IDENTIFIER state until we find a non-alphanumeric
+ if( !is_whitespace(ch) ) {
+ buffer[bufferCount++] = ch;
+ } else {
+ state = SCE_FORTH_DEFAULT;
+ buffer[bufferCount] = '\0';
+#ifdef FORTH_DEBUG
+ fprintf(f_debug,"\nid %s\n",buffer);
+#endif
+
+ // check if the buffer contains a keyword,
+ // and highlight it if it is a keyword...
+// switch(prev_state)
+// case SCE_FORTH_DEFAULT:
+ if(control.InList(buffer)) {
+ styler.ColourTo(i,SCE_FORTH_CONTROL);
+ } else if(keyword.InList(buffer)) {
+ styler.ColourTo(i-1,SCE_FORTH_KEYWORD );
+ } else if(defword.InList(buffer)) {
+ styler.ColourTo(i-1,SCE_FORTH_DEFWORD );
+// prev_state=SCE_FORTH_DEFWORD
+ } else if(preword1.InList(buffer)) {
+ styler.ColourTo(i-1,SCE_FORTH_PREWORD1 );
+// state=SCE_FORTH_PREWORD1;
+ } else if(preword2.InList(buffer)) {
+ styler.ColourTo(i-1,SCE_FORTH_PREWORD2 );
+ } else {
+ styler.ColourTo(i-1,SCE_FORTH_DEFAULT);
+ }
+// break;
+// case
+
+ // push back the faulty character
+ chNext = styler[i--];
+ }
+ break;
+
+ case SCE_FORTH_NUMBER:
+ // stay in CONF_NUMBER state until we find a non-numeric
+ if( isdigit(ch) ) {
+ buffer[bufferCount++] = ch;
+ } else {
+ state = SCE_FORTH_DEFAULT;
+ buffer[bufferCount] = '\0';
+ // Colourize here... (normal number)
+ styler.ColourTo(i-1,SCE_FORTH_NUMBER);
+ // push back a character
+ chNext = styler[i--];
+ }
+ break;
+ }
+ }
+#ifdef FORTH_DEBUG
+ fclose(f_debug);
+#endif
+ delete []buffer;
+*/
+}
+
+static void FoldForthDoc(unsigned int, int, int, WordList *[],
+ Accessor &) {
+}
+
+static const char * const forthWordLists[] = {
+ "control keywords",
+ "keywords",
+ "definition words",
+ "prewords with one argument",
+ "prewords with two arguments",
+ "string definition keywords",
+ 0,
+ };
+
+LexerModule lmForth(SCLEX_FORTH, ColouriseForthDoc, "forth",FoldForthDoc,forthWordLists);
diff --git a/src/LexFortran.cpp b/src/LexFortran.cpp
new file mode 100755
index 0000000..3ab1116
--- /dev/null
+++ b/src/LexFortran.cpp
@@ -0,0 +1,452 @@
+// Scintilla source code edit control
+/** @file LexFortran.cxx
+ ** Lexer for Fortran.
+ ** Writen by Chuan-jian Shen, Last changed Sep. 2003
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+/***************************************/
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+/***************************************/
+#include "Platform.h"
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+/***********************************************/
+static inline bool IsAWordChar(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '%');
+}
+/**********************************************/
+static inline bool IsAWordStart(const int ch) {
+ return (ch < 0x80) && (isalnum(ch));
+}
+/***************************************/
+inline bool IsABlank(unsigned int ch) {
+ return (ch == ' ') || (ch == 0x09) || (ch == 0x0b) ;
+}
+/***************************************/
+inline bool IsALineEnd(char ch) {
+ return ((ch == '\n') || (ch == '\r')) ;
+}
+/***************************************/
+unsigned int GetContinuedPos(unsigned int pos, Accessor &styler) {
+ while (!IsALineEnd(styler.SafeGetCharAt(pos++))) continue;
+ if (styler.SafeGetCharAt(pos) == '\n') pos++;
+ while (IsABlank(styler.SafeGetCharAt(pos++))) continue;
+ char chCur = styler.SafeGetCharAt(pos);
+ if (chCur == '&') {
+ while (IsABlank(styler.SafeGetCharAt(++pos))) continue;
+ return pos;
+ } else {
+ return pos;
+ }
+}
+/***************************************/
+static void ColouriseFortranDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler, bool isFixFormat) {
+ WordList &keywords = *keywordlists[0];
+ WordList &keywords2 = *keywordlists[1];
+ WordList &keywords3 = *keywordlists[2];
+ /***************************************/
+ int posLineStart = 0, numNonBlank = 0, prevState = 0;
+ int endPos = startPos + length;
+ /***************************************/
+ // backtrack to the nearest keyword
+ while ((startPos > 1) && (styler.StyleAt(startPos) != SCE_F_WORD)) {
+ startPos--;
+ }
+ startPos = styler.LineStart(styler.GetLine(startPos));
+ initStyle = styler.StyleAt(startPos - 1);
+ StyleContext sc(startPos, endPos-startPos, initStyle, styler);
+ /***************************************/
+ for (; sc.More(); sc.Forward()) {
+ // remember the start position of the line
+ if (sc.atLineStart) {
+ posLineStart = sc.currentPos;
+ numNonBlank = 0;
+ sc.SetState(SCE_F_DEFAULT);
+ }
+ if (!IsASpaceOrTab(sc.ch)) numNonBlank ++;
+ /***********************************************/
+ // Handle the fix format generically
+ int toLineStart = sc.currentPos - posLineStart;
+ if (isFixFormat && (toLineStart < 6 || toLineStart > 72)) {
+ if (toLineStart == 0 && (tolower(sc.ch) == 'c' || sc.ch == '*') || sc.ch == '!') {
+ sc.SetState(SCE_F_COMMENT);
+ while (!sc.atLineEnd && sc.More()) sc.Forward(); // Until line end
+ } else if (toLineStart > 72) {
+ sc.SetState(SCE_F_COMMENT);
+ while (!sc.atLineEnd && sc.More()) sc.Forward(); // Until line end
+ } else if (toLineStart < 5) {
+ if (IsADigit(sc.ch))
+ sc.SetState(SCE_F_LABEL);
+ else
+ sc.SetState(SCE_F_DEFAULT);
+ } else if (toLineStart == 5) {
+ if (!IsASpace(sc.ch) && sc.ch != '0') {
+ sc.SetState(SCE_F_CONTINUATION);
+ sc.ForwardSetState(prevState);
+ } else
+ sc.SetState(SCE_F_DEFAULT);
+ }
+ continue;
+ }
+ /***************************************/
+ // Handle line continuation generically.
+ if (!isFixFormat && sc.ch == '&') {
+ char chTemp = ' ';
+ int j = 1;
+ while (IsABlank(chTemp) && j<132) {
+ chTemp = static_cast<char>(sc.GetRelative(j));
+ j++;
+ }
+ if (chTemp == '!') {
+ sc.SetState(SCE_F_CONTINUATION);
+ if (sc.chNext == '!') sc.ForwardSetState(SCE_F_COMMENT);
+ } else if (chTemp == '\r' || chTemp == '\n') {
+ int currentState = sc.state;
+ sc.SetState(SCE_F_CONTINUATION);
+ sc.ForwardSetState(SCE_F_DEFAULT);
+ while (IsASpace(sc.ch) && sc.More()) sc.Forward();
+ if (sc.ch == '&') {
+ sc.SetState(SCE_F_CONTINUATION);
+ sc.Forward();
+ }
+ sc.SetState(currentState);
+ }
+ }
+ /***************************************/
+ // Determine if the current state should terminate.
+ if (sc.state == SCE_F_OPERATOR) {
+ sc.SetState(SCE_F_DEFAULT);
+ } else if (sc.state == SCE_F_NUMBER) {
+ if (!(IsAWordChar(sc.ch) || sc.ch=='\'' || sc.ch=='\"' || sc.ch=='.')) {
+ sc.SetState(SCE_F_DEFAULT);
+ }
+ } else if (sc.state == SCE_F_IDENTIFIER) {
+ if (!IsAWordChar(sc.ch) || (sc.ch == '%')) {
+ char s[100];
+ sc.GetCurrentLowered(s, sizeof(s));
+ if (keywords.InList(s)) {
+ sc.ChangeState(SCE_F_WORD);
+ } else if (keywords2.InList(s)) {
+ sc.ChangeState(SCE_F_WORD2);
+ } else if (keywords3.InList(s)) {
+ sc.ChangeState(SCE_F_WORD3);
+ }
+ sc.SetState(SCE_F_DEFAULT);
+ }
+ } else if (sc.state == SCE_F_COMMENT || sc.state == SCE_F_PREPROCESSOR) {
+ if (sc.ch == '\r' || sc.ch == '\n') {
+ sc.SetState(SCE_F_DEFAULT);
+ }
+ } else if (sc.state == SCE_F_STRING1) {
+ prevState = sc.state;
+ if (sc.ch == '\'') {
+ if (sc.chNext == '\'') {
+ sc.Forward();
+ } else {
+ sc.ForwardSetState(SCE_F_DEFAULT);
+ prevState = SCE_F_DEFAULT;
+ }
+ } else if (sc.atLineEnd) {
+ sc.ChangeState(SCE_F_STRINGEOL);
+ sc.ForwardSetState(SCE_F_DEFAULT);
+ }
+ } else if (sc.state == SCE_F_STRING2) {
+ prevState = sc.state;
+ if (sc.atLineEnd) {
+ sc.ChangeState(SCE_F_STRINGEOL);
+ sc.ForwardSetState(SCE_F_DEFAULT);
+ } else if (sc.ch == '\"') {
+ if (sc.chNext == '\"') {
+ sc.Forward();
+ } else {
+ sc.ForwardSetState(SCE_F_DEFAULT);
+ prevState = SCE_F_DEFAULT;
+ }
+ }
+ } else if (sc.state == SCE_F_OPERATOR2) {
+ if (sc.ch == '.') {
+ sc.ForwardSetState(SCE_F_DEFAULT);
+ }
+ } else if (sc.state == SCE_F_CONTINUATION) {
+ sc.SetState(SCE_F_DEFAULT);
+ } else if (sc.state == SCE_F_LABEL) {
+ if (!IsADigit(sc.ch)) {
+ sc.SetState(SCE_F_DEFAULT);
+ } else {
+ if (isFixFormat && sc.currentPos-posLineStart > 4)
+ sc.SetState(SCE_F_DEFAULT);
+ else if (numNonBlank > 5)
+ sc.SetState(SCE_F_DEFAULT);
+ }
+ }
+ /***************************************/
+ // Determine if a new state should be entered.
+ if (sc.state == SCE_F_DEFAULT) {
+ if (sc.ch == '!') {
+ if (sc.chNext == '$') {
+ sc.SetState(SCE_F_PREPROCESSOR);
+ } else {
+ sc.SetState(SCE_F_COMMENT);
+ }
+ } else if ((!isFixFormat) && IsADigit(sc.ch) && numNonBlank == 1) {
+ sc.SetState(SCE_F_LABEL);
+ } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
+ sc.SetState(SCE_F_NUMBER);
+ } else if ((tolower(sc.ch) == 'b' || tolower(sc.ch) == 'o' ||
+ tolower(sc.ch) == 'z') && (sc.chNext == '\"' || sc.chNext == '\'')) {
+ sc.SetState(SCE_F_NUMBER);
+ sc.Forward();
+ } else if (sc.ch == '.' && isalpha(sc.chNext)) {
+ sc.SetState(SCE_F_OPERATOR2);
+ } else if (IsAWordStart(sc.ch)) {
+ sc.SetState(SCE_F_IDENTIFIER);
+ } else if (sc.ch == '\"') {
+ sc.SetState(SCE_F_STRING2);
+ } else if (sc.ch == '\'') {
+ sc.SetState(SCE_F_STRING1);
+ } else if (isoperator(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_F_OPERATOR);
+ }
+ }
+ }
+ sc.Complete();
+}
+/***************************************/
+// To determine the folding level depending on keywords
+static int classifyFoldPointFortran(const char* s, const char* prevWord, const char chNextNonBlank) {
+ int lev = 0;
+ if ((strcmp(prevWord, "else") == 0 && strcmp(s, "if") == 0) || strcmp(s, "elseif") == 0)
+ return -1;
+ if (strcmp(s, "associate") == 0 || strcmp(s, "block") == 0
+ || strcmp(s, "blockdata") == 0 || strcmp(s, "select") == 0
+ || strcmp(s, "do") == 0 || strcmp(s, "enum") ==0
+ || strcmp(s, "function") == 0 || strcmp(s, "interface") == 0
+ || strcmp(s, "module") == 0 || strcmp(s, "program") == 0
+ || strcmp(s, "subroutine") == 0 || strcmp(s, "then") == 0
+ || (strcmp(s, "type") == 0 && chNextNonBlank != '(') ){
+ if (strcmp(prevWord, "end") == 0)
+ lev = 0;
+ else
+ lev = 1;
+ } else if (strcmp(s, "end") == 0 && chNextNonBlank != '='
+ || strcmp(s, "endassociate") == 0 || strcmp(s, "endblock") == 0
+ || strcmp(s, "endblockdata") == 0 || strcmp(s, "endselect") == 0
+ || strcmp(s, "enddo") == 0 || strcmp(s, "endenum") ==0
+ || strcmp(s, "endif") == 0 || strcmp(s, "endforall") == 0
+ || strcmp(s, "endfunction") == 0 || strcmp(s, "endinterface") == 0
+ || strcmp(s, "endmodule") == 0 || strcmp(s, "endprogram") == 0
+ || strcmp(s, "endsubroutine") == 0 || strcmp(s, "endtype") == 0
+ || strcmp(s, "endwhere") == 0
+ || strcmp(s, "procedure") == 0 ) { // Take care of the module procedure statement
+ lev = -1;
+ } else if (strcmp(prevWord, "end") == 0 && strcmp(s, "if") == 0){ // end if
+ lev = 0;
+ }
+ return lev;
+}
+// Folding the code
+static void FoldFortranDoc(unsigned int startPos, int length, int initStyle,
+ Accessor &styler, bool isFixFormat) {
+ //
+ // bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
+ // Do not know how to fold the comment at the moment.
+ //
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ unsigned int endPos = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
+ int levelCurrent = levelPrev;
+ char chNext = styler[startPos];
+ char chNextNonBlank;
+ int styleNext = styler.StyleAt(startPos);
+ int style = initStyle;
+ /***************************************/
+ int lastStart = 0;
+ char prevWord[32] = "";
+ char Label[6] = "";
+ // Variables for do label folding.
+ static int doLabels[100];
+ static int posLabel=-1;
+ /***************************************/
+ for (unsigned int i = startPos; i < endPos; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ chNextNonBlank = chNext;
+ unsigned int j=i+1;
+ while(IsABlank(chNextNonBlank) && j<endPos) {
+ j ++ ;
+ chNextNonBlank = styler.SafeGetCharAt(j);
+ }
+ int stylePrev = style;
+ style = styleNext;
+ styleNext = styler.StyleAt(i + 1);
+ bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
+ //
+ if (stylePrev == SCE_F_DEFAULT && (style == SCE_F_WORD || style == SCE_F_LABEL)) {
+ // Store last word and label start point.
+ lastStart = i;
+ }
+ /***************************************/
+ if (style == SCE_F_WORD) {
+ if(iswordchar(ch) && !iswordchar(chNext)) {
+ char s[32];
+ unsigned int k;
+ for(k=0; (k<31 ) && (k<i-lastStart+1 ); k++) {
+ s[k] = static_cast<char>(tolower(styler[lastStart+k]));
+ }
+ s[k] = '\0';
+ // Handle the forall and where statement and structure.
+ if (strcmp(s, "forall") == 0 || strcmp(s, "where") == 0) {
+ if (strcmp(prevWord, "end") != 0) {
+ j = i + 1;
+ char chBrace = '(', chSeek = ')', ch1 = styler.SafeGetCharAt(j);
+ // Find the position of the first (
+ while (ch1 != chBrace && j<endPos) {
+ j++;
+ ch1 = styler.SafeGetCharAt(j);
+ }
+ char styBrace = styler.StyleAt(j);
+ int depth = 1;
+ char chAtPos;
+ char styAtPos;
+ while (j<endPos) {
+ j++;
+ chAtPos = styler.SafeGetCharAt(j);
+ styAtPos = styler.StyleAt(j);
+ if (styAtPos == styBrace) {
+ if (chAtPos == chBrace) depth++;
+ if (chAtPos == chSeek) depth--;
+ if (depth == 0) break;
+ }
+ }
+ while (j<endPos) {
+ j++;
+ chAtPos = styler.SafeGetCharAt(j);
+ styAtPos = styler.StyleAt(j);
+ if (styAtPos == SCE_F_COMMENT || IsABlank(chAtPos)) continue;
+ if (isFixFormat) {
+ if (!IsALineEnd(chAtPos)) {
+ break;
+ } else {
+ if (lineCurrent < styler.GetLine(styler.Length()-1)) {
+ j = styler.LineStart(lineCurrent+1);
+ if (styler.StyleAt(j+5) == SCE_F_CONTINUATION) {
+ j += 5;
+ continue;
+ } else {
+ levelCurrent++;
+ break;
+ }
+ }
+ }
+ } else {
+ if (chAtPos == '&' && styler.StyleAt(j) == SCE_F_CONTINUATION) {
+ j = GetContinuedPos(j+1, styler);
+ continue;
+ } else if (IsALineEnd(chAtPos)) {
+ levelCurrent ++;
+ break;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ } else {
+ levelCurrent += classifyFoldPointFortran(s, prevWord, chNextNonBlank);
+ // Store the do Labels into array
+ if (strcmp(s, "do") == 0 && IsADigit(chNextNonBlank)) {
+ unsigned int k = 0;
+ for (i=j; (i<j+5 && i<endPos); i++) {
+ ch = styler.SafeGetCharAt(i);
+ if (IsADigit(ch))
+ Label[k++] = ch;
+ else
+ break;
+ }
+ Label[k] = '\0';
+ posLabel ++;
+ doLabels[posLabel] = atoi(Label);
+ }
+ }
+ strcpy(prevWord, s);
+ }
+ } else if (style == SCE_F_LABEL) {
+ if(IsADigit(ch) && !IsADigit(chNext)) {
+ for(j = 0; ( j < 5 ) && ( j < i-lastStart+1 ); j++) {
+ ch = styler.SafeGetCharAt(lastStart + j);
+ if (IsADigit(ch) && styler.StyleAt(lastStart+j) == SCE_F_LABEL)
+ Label[j] = ch;
+ else
+ break;
+ }
+ Label[j] = '\0';
+ while (doLabels[posLabel] == atoi(Label) && posLabel > -1) {
+ levelCurrent--;
+ posLabel--;
+ }
+ }
+ }
+ if (atEOL) {
+ int lev = levelPrev;
+ if (visibleChars == 0 && foldCompact)
+ 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;
+ strcpy(prevWord, "");
+ }
+ /***************************************/
+ 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 FortranWordLists[] = {
+ "Primary keywords and identifiers",
+ "Intrinsic functions",
+ "Extended and user defined functions",
+ 0,
+};
+/***************************************/
+static void ColouriseFortranDocFreeFormat(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+ Accessor &styler) {
+ ColouriseFortranDoc(startPos, length, initStyle, keywordlists, styler, false);
+}
+/***************************************/
+static void ColouriseFortranDocFixFormat(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+ Accessor &styler) {
+ ColouriseFortranDoc(startPos, length, initStyle, keywordlists, styler, true);
+}
+/***************************************/
+static void FoldFortranDocFreeFormat(unsigned int startPos, int length, int initStyle,
+ WordList *[], Accessor &styler) {
+ FoldFortranDoc(startPos, length, initStyle,styler, false);
+}
+/***************************************/
+static void FoldFortranDocFixFormat(unsigned int startPos, int length, int initStyle,
+ WordList *[], Accessor &styler) {
+ FoldFortranDoc(startPos, length, initStyle,styler, true);
+}
+/***************************************/
+LexerModule lmFortran(SCLEX_FORTRAN, ColouriseFortranDocFreeFormat, "fortran", FoldFortranDocFreeFormat, FortranWordLists);
+LexerModule lmF77(SCLEX_F77, ColouriseFortranDocFixFormat, "f77", FoldFortranDocFixFormat, FortranWordLists);
diff --git a/src/LexGen.py b/src/LexGen.py
new file mode 100755
index 0000000..97a40b4
--- /dev/null
+++ b/src/LexGen.py
@@ -0,0 +1,241 @@
+# LexGen.py - implemented 2002 by Neil Hodgson neilh@scintilla.org
+# Released to the public domain.
+
+# Regenerate the Scintilla and SciTE source files that list
+# all the lexers and all the properties files.
+# Should be run whenever a new lexer is added or removed.
+# Requires Python 2.1 or later
+# Most files are regenerated in place with templates stored in comments.
+# The VS .NET project file is generated into a different file as the
+# VS .NET environment will not retain comments when modifying the file.
+# The files are copied to a string apart from sections between a
+# ++Autogenerated comment and a --Autogenerated comment which is
+# generated by the CopyWithInsertion function. After the whole
+# string is instantiated, it is compared with the target file and
+# if different the file is rewritten.
+# Does not regenerate the Visual C++ 6 project files but does the VS .NET
+# project file.
+
+import string
+import sys
+import os
+import glob
+
+# EOL constants
+CR = "\r"
+LF = "\n"
+CRLF = "\r\n"
+if sys.platform == "win32":
+ NATIVE = CRLF
+else:
+ # Yes, LF is the native EOL even on Mac OS X. CR is just for
+ # Mac OS <=9 (a.k.a. "Mac Classic")
+ NATIVE = LF
+
+# Automatically generated sections contain start and end comments,
+# a definition line and the results.
+# The results are replaced by regenerating based on the definition line.
+# The definition line is a comment prefix followed by "**".
+# If there is a digit after the ** then this indicates which list to use
+# and the digit and next character are not part of the definition
+# Backslash is used as an escape within the definition line.
+# The part between \( and \) is repeated for each item in the list.
+# \* is replaced by each list item. \t, and \n are tab and newline.
+def CopyWithInsertion(input, commentPrefix, retainDefs, eolType, *lists):
+ copying = 1
+ listid = 0
+ output = []
+ for line in input.splitlines(0):
+ isStartGenerated = line.startswith(commentPrefix + "++Autogenerated")
+ if copying and not isStartGenerated:
+ output.append(line)
+ if isStartGenerated:
+ if retainDefs:
+ output.append(line)
+ copying = 0
+ definition = ""
+ elif not copying and line.startswith(commentPrefix + "**"):
+ if retainDefs:
+ output.append(line)
+ definition = line[len(commentPrefix + "**"):]
+ listid = 0
+ if definition[0] in string.digits:
+ listid = int(definition[:1])
+ definition = definition[2:]
+ # Hide double slashes as a control character
+ definition = definition.replace("\\\\", "\001")
+ # Do some normal C style transforms
+ definition = definition.replace("\\n", "\n")
+ definition = definition.replace("\\t", "\t")
+ # Get the doubled backslashes back as single backslashes
+ definition = definition.replace("\001", "\\")
+ startRepeat = definition.find("\\(")
+ endRepeat = definition.find("\\)")
+ intro = definition[:startRepeat]
+ out = ""
+ if intro.endswith("\n"):
+ pos = 0
+ else:
+ pos = len(intro)
+ out += intro
+ middle = definition[startRepeat+2:endRepeat]
+ for i in lists[listid]:
+ item = middle.replace("\\*", i)
+ if pos and (pos + len(item) >= 80):
+ out += "\\\n"
+ pos = 0
+ out += item
+ pos += len(item)
+ if item.endswith("\n"):
+ pos = 0
+ outro = definition[endRepeat+2:]
+ out += outro
+ out = out.replace("\n", eolType) # correct EOLs in generated content
+ output.append(out)
+ elif line.startswith(commentPrefix + "--Autogenerated"):
+ copying = 1
+ if retainDefs:
+ output.append(line)
+ output = [line.rstrip(" \t") for line in output] # trim trailing whitespace
+ return eolType.join(output) + eolType
+
+def UpdateFile(filename, updated):
+ """ If the file is different to updated then copy updated
+ into the file else leave alone so CVS and make don't treat
+ it as modified. """
+ try:
+ infile = open(filename, "rb")
+ except IOError: # File is not there yet
+ out = open(filename, "wb")
+ out.write(updated)
+ out.close()
+ print "New", filename
+ return
+ original = infile.read()
+ infile.close()
+ if updated != original:
+ os.unlink(filename)
+ out = open(filename, "wb")
+ out.write(updated)
+ out.close()
+ print "Changed", filename
+ #~ else:
+ #~ print "Unchanged", filename
+
+def Generate(inpath, outpath, commentPrefix, eolType, *lists):
+ """Generate 'outpath' from 'inpath'.
+
+ "eolType" indicates the type of EOLs to use in the generated
+ file. It should be one of following constants: LF, CRLF,
+ CR, or NATIVE.
+ """
+ #print "generate '%s' -> '%s' (comment prefix: %r, eols: %r)"\
+ # % (inpath, outpath, commentPrefix, eolType)
+ try:
+ infile = open(inpath, "r")
+ except IOError:
+ print "Can not open", inpath
+ return
+ original = infile.read()
+ infile.close()
+ updated = CopyWithInsertion(original, commentPrefix,
+ inpath == outpath, eolType, *lists)
+ UpdateFile(outpath, updated)
+
+def Regenerate(filename, commentPrefix, eolType, *lists):
+ """Regenerate the given file.
+
+ "eolType" indicates the type of EOLs to use in the generated
+ file. It should be one of following constants: LF, CRLF,
+ CR, or NATIVE.
+ """
+ Generate(filename, filename, commentPrefix, eolType, *lists)
+
+def FindModules(lexFile):
+ modules = []
+ f = open(lexFile)
+ for l in f.readlines():
+ if l.startswith("LexerModule"):
+ l = l.replace("(", " ")
+ modules.append(l.split()[1])
+ return modules
+
+knownIrregularProperties = [
+ "fold",
+ "styling.within.preprocessor",
+ "tab.timmy.whinge.level",
+ "asp.default.language",
+ "html.tags.case.sensitive",
+ "ps.level",
+ "ps.tokenize",
+ "sql.backslash.escapes",
+ "nsis.uservars",
+ "nsis.ignorecase"
+]
+
+def FindProperties(lexFile):
+ properties = set()
+ f = open(lexFile)
+ for l in f.readlines():
+ if "GetProperty" in l:
+ l = l.strip()
+ if not l.startswith("//"): # Drop comments
+ propertyName = l.split("\"")[1]
+ if propertyName.lower() == propertyName:
+ # Only allow lower case property names
+ if propertyName in knownIrregularProperties or \
+ propertyName.startswith("fold.") or \
+ propertyName.startswith("lexer."):
+ properties.add(propertyName)
+ return properties
+
+def ciCompare(a,b):
+ return cmp(a.lower(), b.lower())
+
+def RegenerateAll():
+ root="../../"
+
+ # Find all the lexer source code files
+ lexFilePaths = glob.glob(root + "scintilla/src/Lex*.cxx")
+ lexFiles = [os.path.basename(f)[:-4] for f in lexFilePaths]
+ print lexFiles
+ lexerModules = []
+ lexerProperties = set()
+ for lexFile in lexFilePaths:
+ lexerModules.extend(FindModules(lexFile))
+ lexerProperties.update(FindProperties(lexFile))
+ lexerModules.sort(ciCompare)
+ lexerProperties.remove("fold.comment.python")
+ lexerProperties = list(lexerProperties)
+ lexerProperties.sort(ciCompare)
+
+ # Find all the SciTE properties files
+ otherProps = ["abbrev.properties", "Embedded.properties", "SciTEGlobal.properties", "SciTE.properties"]
+ propFilePaths = glob.glob(root + "scite/src/*.properties")
+ propFiles = [os.path.basename(f) for f in propFilePaths if os.path.basename(f) not in otherProps]
+ propFiles.sort(ciCompare)
+ print propFiles
+
+ # Find all the menu command IDs in the SciTE header
+ SciTEHeader = file(root + "scite/src/SciTE.h")
+ lines = SciTEHeader.read().split("\n")
+ SciTEHeader.close()
+ ids = [id for id in [l.split()[1] for l in lines if l.startswith("#define")] if id.startswith("IDM_")]
+ #print ids
+
+ Regenerate(root + "scintilla/src/KeyWords.cxx", "//", NATIVE, lexerModules)
+ Regenerate(root + "scintilla/win32/makefile", "#", NATIVE, lexFiles)
+ Regenerate(root + "scintilla/win32/scintilla.mak", "#", NATIVE, lexFiles)
+ Regenerate(root + "scintilla/win32/scintilla_vc6.mak", "#", NATIVE, lexFiles)
+ # Use Unix EOLs for gtk Makefiles so they work for Linux users when
+ # extracted from the Scintilla source ZIP (typically created on
+ # Windows).
+ Regenerate(root + "scintilla/gtk/makefile", "#", LF, lexFiles)
+ Regenerate(root + "scintilla/gtk/scintilla.mak", "#", NATIVE, lexFiles)
+ Regenerate(root + "scite/win32/makefile", "#", NATIVE, lexFiles, propFiles)
+ Regenerate(root + "scite/win32/scite.mak", "#", NATIVE, lexFiles, propFiles)
+ Regenerate(root + "scite/src/SciTEProps.cxx", "//", NATIVE, lexerProperties, ids)
+ Generate(root + "scite/boundscheck/vcproj.gen",
+ root + "scite/boundscheck/SciTE.vcproj", "#", NATIVE, lexFiles)
+
+RegenerateAll()
diff --git a/src/LexGui4Cli.cpp b/src/LexGui4Cli.cpp
new file mode 100755
index 0000000..f76fff6
--- /dev/null
+++ b/src/LexGui4Cli.cpp
@@ -0,0 +1,309 @@
+// Scintilla source code edit control
+// Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
+/*
+This is the Lexer for Gui4Cli, included in SciLexer.dll
+- by d. Keletsekis, 2/10/2003
+
+To add to SciLexer.dll:
+1. Add the values below to INCLUDE\Scintilla.iface
+2. Run the include/HFacer.py script
+3. Run the src/lexGen.py script
+
+val SCE_GC_DEFAULT=0
+val SCE_GC_COMMENTLINE=1
+val SCE_GC_COMMENTBLOCK=2
+val SCE_GC_GLOBAL=3
+val SCE_GC_EVENT=4
+val SCE_GC_ATTRIBUTE=5
+val SCE_GC_CONTROL=6
+val SCE_GC_COMMAND=7
+val SCE_GC_STRING=8
+val SCE_GC_OPERATOR=9
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+#define debug Platform::DebugPrintf
+
+static inline bool IsAWordChar(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_' || ch =='\\');
+}
+
+static inline bool IsAWordStart(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '.');
+}
+
+inline bool isGCOperator(int ch)
+{ if (isalnum(ch))
+ return false;
+ // '.' left out as it is used to make up numbers
+ if (ch == '*' || ch == '/' || ch == '-' || ch == '+' ||
+ ch == '(' || ch == ')' || ch == '=' || ch == '%' ||
+ ch == '[' || ch == ']' || ch == '<' || ch == '>' ||
+ ch == ',' || ch == ';' || ch == ':')
+ return true;
+ return false;
+}
+
+#define isSpace(x) ((x)==' ' || (x)=='\t')
+#define isNL(x) ((x)=='\n' || (x)=='\r')
+#define isSpaceOrNL(x) (isSpace(x) || isNL(x))
+#define BUFFSIZE 500
+#define isFoldPoint(x) ((styler.LevelAt(x) & SC_FOLDLEVELNUMBERMASK) == 1024)
+
+static void colorFirstWord(WordList *keywordlists[], Accessor &styler,
+ StyleContext *sc, char *buff, int length, int)
+{
+ int c = 0;
+ while (sc->More() && isSpaceOrNL(sc->ch))
+ { sc->Forward();
+ }
+ styler.ColourTo(sc->currentPos - 1, sc->state);
+
+ if (!IsAWordChar(sc->ch)) // comment, marker, etc..
+ return;
+
+ while (sc->More() && !isSpaceOrNL(sc->ch) && (c < length-1) && !isGCOperator(sc->ch))
+ { buff[c] = static_cast<char>(sc->ch);
+ ++c; sc->Forward();
+ }
+ buff[c] = '\0';
+ char *p = buff;
+ while (*p) // capitalize..
+ { if (islower(*p)) *p = static_cast<char>(toupper(*p));
+ ++p;
+ }
+
+ WordList &kGlobal = *keywordlists[0]; // keyword lists set by the user
+ WordList &kEvent = *keywordlists[1];
+ WordList &kAttribute = *keywordlists[2];
+ WordList &kControl = *keywordlists[3];
+ WordList &kCommand = *keywordlists[4];
+
+ int state = 0;
+ // int level = styler.LevelAt(line) & SC_FOLDLEVELNUMBERMASK;
+ // debug ("line = %d, level = %d", line, level);
+
+ if (kGlobal.InList(buff)) state = SCE_GC_GLOBAL;
+ else if (kAttribute.InList(buff)) state = SCE_GC_ATTRIBUTE;
+ else if (kControl.InList(buff)) state = SCE_GC_CONTROL;
+ else if (kCommand.InList(buff)) state = SCE_GC_COMMAND;
+ else if (kEvent.InList(buff)) state = SCE_GC_EVENT;
+
+ if (state)
+ { sc->ChangeState(state);
+ styler.ColourTo(sc->currentPos - 1, sc->state);
+ sc->ChangeState(SCE_GC_DEFAULT);
+ }
+ else
+ { sc->ChangeState(SCE_GC_DEFAULT);
+ styler.ColourTo(sc->currentPos - 1, sc->state);
+ }
+}
+
+// Main colorizing function called by Scintilla
+static void
+ColouriseGui4CliDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler)
+{
+ styler.StartAt(startPos);
+
+ int quotestart = 0, oldstate, currentline = styler.GetLine(startPos);
+ styler.StartSegment(startPos);
+ bool noforward;
+ char buff[BUFFSIZE+1]; // buffer for command name
+
+ StyleContext sc(startPos, length, initStyle, styler);
+ buff[0] = '\0'; // cbuff = 0;
+
+ if (sc.state != SCE_GC_COMMENTBLOCK) // colorize 1st word..
+ colorFirstWord(keywordlists, styler, &sc, buff, BUFFSIZE, currentline);
+
+ while (sc.More())
+ { noforward = 0;
+
+ switch (sc.ch)
+ {
+ case '/':
+ if (sc.state == SCE_GC_COMMENTBLOCK || sc.state == SCE_GC_STRING)
+ break;
+ if (sc.chNext == '/') // line comment
+ { sc.SetState (SCE_GC_COMMENTLINE);
+ sc.Forward();
+ styler.ColourTo(sc.currentPos, sc.state);
+ }
+ else if (sc.chNext == '*') // block comment
+ { sc.SetState(SCE_GC_COMMENTBLOCK);
+ sc.Forward();
+ styler.ColourTo(sc.currentPos, sc.state);
+ }
+ else
+ styler.ColourTo(sc.currentPos, sc.state);
+ break;
+
+ case '*': // end of comment block, or operator..
+ if (sc.state == SCE_GC_STRING)
+ break;
+ if (sc.state == SCE_GC_COMMENTBLOCK && sc.chNext == '/')
+ { sc.Forward();
+ styler.ColourTo(sc.currentPos, sc.state);
+ sc.ChangeState (SCE_GC_DEFAULT);
+ }
+ else
+ styler.ColourTo(sc.currentPos, sc.state);
+ break;
+
+ case '\'': case '\"': // strings..
+ if (sc.state == SCE_GC_COMMENTBLOCK || sc.state == SCE_GC_COMMENTLINE)
+ break;
+ if (sc.state == SCE_GC_STRING)
+ { if (sc.ch == quotestart) // match same quote char..
+ { styler.ColourTo(sc.currentPos, sc.state);
+ sc.ChangeState(SCE_GC_DEFAULT);
+ quotestart = 0;
+ } }
+ else
+ { styler.ColourTo(sc.currentPos - 1, sc.state);
+ sc.ChangeState(SCE_GC_STRING);
+ quotestart = sc.ch;
+ }
+ break;
+
+ case ';': // end of commandline character
+ if (sc.state != SCE_GC_COMMENTBLOCK && sc.state != SCE_GC_COMMENTLINE &&
+ sc.state != SCE_GC_STRING)
+ {
+ styler.ColourTo(sc.currentPos - 1, sc.state);
+ styler.ColourTo(sc.currentPos, SCE_GC_OPERATOR);
+ sc.ChangeState(SCE_GC_DEFAULT);
+ sc.Forward();
+ colorFirstWord(keywordlists, styler, &sc, buff, BUFFSIZE, currentline);
+ noforward = 1; // don't move forward - already positioned at next char..
+ }
+ break;
+
+ case '+': case '-': case '=': case '!': // operators..
+ case '<': case '>': case '&': case '|': case '$':
+ if (sc.state != SCE_GC_COMMENTBLOCK && sc.state != SCE_GC_COMMENTLINE &&
+ sc.state != SCE_GC_STRING)
+ {
+ styler.ColourTo(sc.currentPos - 1, sc.state);
+ styler.ColourTo(sc.currentPos, SCE_GC_OPERATOR);
+ sc.ChangeState(SCE_GC_DEFAULT);
+ }
+ break;
+
+ case '\\': // escape - same as operator, but also mark in strings..
+ if (sc.state != SCE_GC_COMMENTBLOCK && sc.state != SCE_GC_COMMENTLINE)
+ {
+ oldstate = sc.state;
+ styler.ColourTo(sc.currentPos - 1, sc.state);
+ sc.Forward(); // mark also the next char..
+ styler.ColourTo(sc.currentPos, SCE_GC_OPERATOR);
+ sc.ChangeState(oldstate);
+ }
+ break;
+
+ case '\n': case '\r':
+ ++currentline;
+ if (sc.state == SCE_GC_COMMENTLINE)
+ { styler.ColourTo(sc.currentPos, sc.state);
+ sc.ChangeState (SCE_GC_DEFAULT);
+ }
+ else if (sc.state != SCE_GC_COMMENTBLOCK)
+ { colorFirstWord(keywordlists, styler, &sc, buff, BUFFSIZE, currentline);
+ noforward = 1; // don't move forward - already positioned at next char..
+ }
+ break;
+
+// case ' ': case '\t':
+// default :
+ }
+
+ if (!noforward) sc.Forward();
+
+ }
+ sc.Complete();
+}
+
+// Main folding function called by Scintilla - (based on props (.ini) files function)
+static void FoldGui4Cli(unsigned int startPos, int length, int,
+ WordList *[], Accessor &styler)
+{
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+
+ unsigned int endPos = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+
+ char chNext = styler[startPos];
+ int styleNext = styler.StyleAt(startPos);
+ bool headerPoint = false;
+
+ for (unsigned int i = startPos; i < endPos; i++)
+ {
+ char ch = chNext;
+ chNext = styler[i+1];
+
+ int style = styleNext;
+ styleNext = styler.StyleAt(i + 1);
+ bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
+
+ if (style == SCE_GC_EVENT || style == SCE_GC_GLOBAL)
+ { headerPoint = true; // fold at events and globals
+ }
+
+ if (atEOL)
+ { int lev = SC_FOLDLEVELBASE+1;
+
+ if (headerPoint)
+ lev = SC_FOLDLEVELBASE;
+
+ if (visibleChars == 0 && foldCompact)
+ lev |= SC_FOLDLEVELWHITEFLAG;
+
+ if (headerPoint)
+ lev |= SC_FOLDLEVELHEADERFLAG;
+
+ if (lev != styler.LevelAt(lineCurrent)) // set level, if not already correct
+ { styler.SetLevel(lineCurrent, lev);
+ }
+
+ lineCurrent++; // re-initialize our flags
+ visibleChars = 0;
+ headerPoint = false;
+ }
+
+ if (!(isspacechar(ch))) // || (style == SCE_GC_COMMENTLINE) || (style != SCE_GC_COMMENTBLOCK)))
+ visibleChars++;
+ }
+
+ int lev = headerPoint ? SC_FOLDLEVELBASE : SC_FOLDLEVELBASE+1;
+ int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
+ styler.SetLevel(lineCurrent, lev | flagsNext);
+}
+
+// I have no idea what these are for.. probably accessible by some message.
+static const char * const gui4cliWordListDesc[] = {
+ "Globals", "Events", "Attributes", "Control", "Commands",
+ 0
+};
+
+// Declare language & pass our function pointers to Scintilla
+LexerModule lmGui4Cli(SCLEX_GUI4CLI, ColouriseGui4CliDoc, "gui4cli", FoldGui4Cli, gui4cliWordListDesc);
+
+#undef debug
+
diff --git a/src/LexHTML.cpp b/src/LexHTML.cpp
new file mode 100755
index 0000000..dad8fce
--- /dev/null
+++ b/src/LexHTML.cpp
@@ -0,0 +1,2042 @@
+// Scintilla source code edit control
+/** @file LexHTML.cxx
+ ** Lexer for HTML.
+ **/
+// Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+#define SCE_HA_JS (SCE_HJA_START - SCE_HJ_START)
+#define SCE_HA_VBS (SCE_HBA_START - SCE_HB_START)
+#define SCE_HA_PYTHON (SCE_HPA_START - SCE_HP_START)
+
+enum script_type { eScriptNone = 0, eScriptJS, eScriptVBS, eScriptPython, eScriptPHP, eScriptXML, eScriptSGML, eScriptSGMLblock };
+enum script_mode { eHtml = 0, eNonHtmlScript, eNonHtmlPreProc, eNonHtmlScriptPreProc };
+
+static inline bool IsAWordChar(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_');
+}
+
+static inline bool IsAWordStart(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '_');
+}
+
+static inline int MakeLowerCase(int ch) {
+ if (ch < 'A' || ch > 'Z')
+ return ch;
+ else
+ return ch - 'A' + 'a';
+}
+
+static void GetTextSegment(Accessor &styler, unsigned int start, unsigned int end, char *s, size_t len) {
+ size_t i = 0;
+ for (; (i < end - start + 1) && (i < len-1); i++) {
+ s[i] = static_cast<char>(MakeLowerCase(styler[start + i]));
+ }
+ s[i] = '\0';
+}
+
+static script_type segIsScriptingIndicator(Accessor &styler, unsigned int start, unsigned int end, script_type prevValue) {
+ char s[100];
+ GetTextSegment(styler, start, end, s, sizeof(s));
+ //Platform::DebugPrintf("Scripting indicator [%s]\n", s);
+ if (strstr(s, "src")) // External script
+ return eScriptNone;
+ if (strstr(s, "vbs"))
+ return eScriptVBS;
+ if (strstr(s, "pyth"))
+ return eScriptPython;
+ if (strstr(s, "javas"))
+ return eScriptJS;
+ if (strstr(s, "jscr"))
+ return eScriptJS;
+ if (strstr(s, "php"))
+ return eScriptPHP;
+ if (strstr(s, "xml"))
+ return eScriptXML;
+
+ return prevValue;
+}
+
+static int PrintScriptingIndicatorOffset(Accessor &styler, unsigned int start, unsigned int end) {
+ int iResult = 0;
+ char s[100];
+ GetTextSegment(styler, start, end, s, sizeof(s));
+ if (0 == strncmp(s, "php", 3)) {
+ iResult = 3;
+ }
+
+ return iResult;
+}
+
+static script_type ScriptOfState(int state) {
+ if ((state >= SCE_HP_START) && (state <= SCE_HP_IDENTIFIER)) {
+ return eScriptPython;
+ } else if ((state >= SCE_HB_START) && (state <= SCE_HB_STRINGEOL)) {
+ return eScriptVBS;
+ } else if ((state >= SCE_HJ_START) && (state <= SCE_HJ_REGEX)) {
+ return eScriptJS;
+ } else if ((state >= SCE_HPHP_DEFAULT) && (state <= SCE_HPHP_COMMENTLINE)) {
+ return eScriptPHP;
+ } else if ((state >= SCE_H_SGML_DEFAULT) && (state < SCE_H_SGML_BLOCK_DEFAULT)) {
+ return eScriptSGML;
+ } else if (state == SCE_H_SGML_BLOCK_DEFAULT) {
+ return eScriptSGMLblock;
+ } else {
+ return eScriptNone;
+ }
+}
+
+static int statePrintForState(int state, script_mode inScriptType) {
+ int StateToPrint;
+
+ if ((state >= SCE_HP_START) && (state <= SCE_HP_IDENTIFIER)) {
+ StateToPrint = state + ((inScriptType == eNonHtmlScript) ? 0 : SCE_HA_PYTHON);
+ } else if ((state >= SCE_HB_START) && (state <= SCE_HB_STRINGEOL)) {
+ StateToPrint = state + ((inScriptType == eNonHtmlScript) ? 0 : SCE_HA_VBS);
+ } else if ((state >= SCE_HJ_START) && (state <= SCE_HJ_REGEX)) {
+ StateToPrint = state + ((inScriptType == eNonHtmlScript) ? 0 : SCE_HA_JS);
+ } else {
+ StateToPrint = state;
+ }
+
+ return StateToPrint;
+}
+
+static int stateForPrintState(int StateToPrint) {
+ int state;
+
+ if ((StateToPrint >= SCE_HPA_START) && (StateToPrint <= SCE_HPA_IDENTIFIER)) {
+ state = StateToPrint - SCE_HA_PYTHON;
+ } else if ((StateToPrint >= SCE_HBA_START) && (StateToPrint <= SCE_HBA_STRINGEOL)) {
+ state = StateToPrint - SCE_HA_VBS;
+ } else if ((StateToPrint >= SCE_HJA_START) && (StateToPrint <= SCE_HJA_REGEX)) {
+ state = StateToPrint - SCE_HA_JS;
+ } else {
+ state = StateToPrint;
+ }
+
+ return state;
+}
+
+static inline bool IsNumber(unsigned int start, Accessor &styler) {
+ return IsADigit(styler[start]) || (styler[start] == '.') ||
+ (styler[start] == '-') || (styler[start] == '#');
+}
+
+static inline bool isStringState(int state) {
+ bool bResult;
+
+ switch (state) {
+ case SCE_HJ_DOUBLESTRING:
+ case SCE_HJ_SINGLESTRING:
+ case SCE_HJA_DOUBLESTRING:
+ case SCE_HJA_SINGLESTRING:
+ case SCE_HB_STRING:
+ case SCE_HBA_STRING:
+ case SCE_HP_STRING:
+ case SCE_HP_CHARACTER:
+ case SCE_HP_TRIPLE:
+ case SCE_HP_TRIPLEDOUBLE:
+ case SCE_HPA_STRING:
+ case SCE_HPA_CHARACTER:
+ case SCE_HPA_TRIPLE:
+ case SCE_HPA_TRIPLEDOUBLE:
+ case SCE_HPHP_HSTRING:
+ case SCE_HPHP_SIMPLESTRING:
+ case SCE_HPHP_HSTRING_VARIABLE:
+ case SCE_HPHP_COMPLEX_VARIABLE:
+ bResult = true;
+ break;
+ default :
+ bResult = false;
+ break;
+ }
+ return bResult;
+}
+
+static inline bool stateAllowsTermination(int state) {
+ bool allowTermination = !isStringState(state);
+ if (allowTermination) {
+ switch (state) {
+ case SCE_HPHP_COMMENT:
+ case SCE_HP_COMMENTLINE:
+ case SCE_HPA_COMMENTLINE:
+ allowTermination = false;
+ }
+ }
+ return allowTermination;
+}
+
+// not really well done, since it's only comments that should lex the %> and <%
+static inline bool isCommentASPState(int state) {
+ bool bResult;
+
+ switch (state) {
+ case SCE_HJ_COMMENT:
+ case SCE_HJ_COMMENTLINE:
+ case SCE_HJ_COMMENTDOC:
+ case SCE_HB_COMMENTLINE:
+ case SCE_HP_COMMENTLINE:
+ case SCE_HPHP_COMMENT:
+ case SCE_HPHP_COMMENTLINE:
+ bResult = true;
+ break;
+ default :
+ bResult = false;
+ break;
+ }
+ return bResult;
+}
+
+static void classifyAttribHTML(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler) {
+ bool wordIsNumber = IsNumber(start, styler);
+ char chAttr = SCE_H_ATTRIBUTEUNKNOWN;
+ if (wordIsNumber) {
+ chAttr = SCE_H_NUMBER;
+ } else {
+ char s[100];
+ GetTextSegment(styler, start, end, s, sizeof(s));
+ if (keywords.InList(s))
+ chAttr = SCE_H_ATTRIBUTE;
+ }
+ if ((chAttr == SCE_H_ATTRIBUTEUNKNOWN) && !keywords)
+ // No keywords -> all are known
+ chAttr = SCE_H_ATTRIBUTE;
+ styler.ColourTo(end, chAttr);
+}
+
+static int classifyTagHTML(unsigned int start, unsigned int end,
+ WordList &keywords, Accessor &styler, bool &tagDontFold,
+ bool caseSensitive) {
+ char s[30 + 2];
+ // Copy after the '<'
+ unsigned int i = 0;
+ for (unsigned int cPos = start; cPos <= end && i < 30; cPos++) {
+ char ch = styler[cPos];
+ if ((ch != '<') && (ch != '/')) {
+ s[i++] = caseSensitive ? ch : static_cast<char>(MakeLowerCase(ch));
+ }
+ }
+
+ //The following is only a quick hack, to see if this whole thing would work
+ //we first need the tagname with a trailing space...
+ s[i] = ' ';
+ s[i+1] = '\0';
+
+ //...to find it in the list of no-container-tags
+ // (There are many more. We will need a keywordlist in the property file for this)
+ tagDontFold = (NULL != strstr("meta link img area br hr input ",s));
+
+ //now we can remove the trailing space
+ s[i] = '\0';
+
+ bool isScript = false;
+ char chAttr = SCE_H_TAGUNKNOWN;
+ if (s[0] == '!') {
+ chAttr = SCE_H_SGML_DEFAULT;
+ } else if (s[0] == '/') { // Closing tag
+ if (keywords.InList(s + 1))
+ chAttr = SCE_H_TAG;
+ } else {
+ if (keywords.InList(s)) {
+ chAttr = SCE_H_TAG;
+ isScript = 0 == strcmp(s, "script");
+ }
+ }
+ if ((chAttr == SCE_H_TAGUNKNOWN) && !keywords) {
+ // No keywords -> all are known
+ chAttr = SCE_H_TAG;
+ isScript = 0 == strcmp(s, "script");
+ }
+ styler.ColourTo(end, chAttr);
+ return isScript ? SCE_H_SCRIPT : chAttr;
+}
+
+static void classifyWordHTJS(unsigned int start, unsigned int end,
+ WordList &keywords, Accessor &styler, script_mode inScriptType) {
+ char chAttr = SCE_HJ_WORD;
+ bool wordIsNumber = IsADigit(styler[start]) || (styler[start] == '.');
+ if (wordIsNumber)
+ chAttr = SCE_HJ_NUMBER;
+ else {
+ char s[30 + 1];
+ unsigned int i = 0;
+ for (; i < end - start + 1 && i < 30; i++) {
+ s[i] = styler[start + i];
+ }
+ s[i] = '\0';
+ if (keywords.InList(s))
+ chAttr = SCE_HJ_KEYWORD;
+ }
+ styler.ColourTo(end, statePrintForState(chAttr, inScriptType));
+}
+
+static int classifyWordHTVB(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler, script_mode inScriptType) {
+ char chAttr = SCE_HB_IDENTIFIER;
+ bool wordIsNumber = IsADigit(styler[start]) || (styler[start] == '.');
+ if (wordIsNumber)
+ chAttr = SCE_HB_NUMBER;
+ else {
+ char s[100];
+ GetTextSegment(styler, start, end, s, sizeof(s));
+ if (keywords.InList(s)) {
+ chAttr = SCE_HB_WORD;
+ if (strcmp(s, "rem") == 0)
+ chAttr = SCE_HB_COMMENTLINE;
+ }
+ }
+ styler.ColourTo(end, statePrintForState(chAttr, inScriptType));
+ if (chAttr == SCE_HB_COMMENTLINE)
+ return SCE_HB_COMMENTLINE;
+ else
+ return SCE_HB_DEFAULT;
+}
+
+static void classifyWordHTPy(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler, char *prevWord, script_mode inScriptType) {
+ bool wordIsNumber = IsADigit(styler[start]);
+ char s[30 + 1];
+ unsigned int i = 0;
+ for (; i < end - start + 1 && i < 30; i++) {
+ s[i] = styler[start + i];
+ }
+ s[i] = '\0';
+ char chAttr = SCE_HP_IDENTIFIER;
+ if (0 == strcmp(prevWord, "class"))
+ chAttr = SCE_HP_CLASSNAME;
+ else if (0 == strcmp(prevWord, "def"))
+ chAttr = SCE_HP_DEFNAME;
+ else if (wordIsNumber)
+ chAttr = SCE_HP_NUMBER;
+ else if (keywords.InList(s))
+ chAttr = SCE_HP_WORD;
+ styler.ColourTo(end, statePrintForState(chAttr, inScriptType));
+ strcpy(prevWord, s);
+}
+
+// Update the word colour to default or keyword
+// Called when in a PHP word
+static void classifyWordHTPHP(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler) {
+ char chAttr = SCE_HPHP_DEFAULT;
+ bool wordIsNumber = IsADigit(styler[start]) || (styler[start] == '.' && start+1 <= end && IsADigit(styler[start+1]));
+ if (wordIsNumber)
+ chAttr = SCE_HPHP_NUMBER;
+ else {
+ char s[100];
+ GetTextSegment(styler, start, end, s, sizeof(s));
+ if (keywords.InList(s))
+ chAttr = SCE_HPHP_WORD;
+ }
+ styler.ColourTo(end, chAttr);
+}
+
+static bool isWordHSGML(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler) {
+ char s[30 + 1];
+ unsigned int i = 0;
+ for (; i < end - start + 1 && i < 30; i++) {
+ s[i] = styler[start + i];
+ }
+ s[i] = '\0';
+ return keywords.InList(s);
+}
+
+static bool isWordCdata(unsigned int start, unsigned int end, Accessor &styler) {
+ char s[30 + 1];
+ unsigned int i = 0;
+ for (; i < end - start + 1 && i < 30; i++) {
+ s[i] = styler[start + i];
+ }
+ s[i] = '\0';
+ return (0 == strcmp(s, "[CDATA["));
+}
+
+// Return the first state to reach when entering a scripting language
+static int StateForScript(script_type scriptLanguage) {
+ int Result;
+ switch (scriptLanguage) {
+ case eScriptVBS:
+ Result = SCE_HB_START;
+ break;
+ case eScriptPython:
+ Result = SCE_HP_START;
+ break;
+ case eScriptPHP:
+ Result = SCE_HPHP_DEFAULT;
+ break;
+ case eScriptXML:
+ Result = SCE_H_TAGUNKNOWN;
+ break;
+ case eScriptSGML:
+ Result = SCE_H_SGML_DEFAULT;
+ break;
+ default :
+ Result = SCE_HJ_START;
+ break;
+ }
+ return Result;
+}
+
+static inline bool ishtmlwordchar(char ch) {
+ return !isascii(ch) ||
+ (isalnum(ch) || ch == '.' || ch == '-' || ch == '_' || ch == ':' || ch == '!' || ch == '#');
+}
+
+static inline bool issgmlwordchar(char ch) {
+ return !isascii(ch) ||
+ (isalnum(ch) || ch == '.' || ch == '_' || ch == ':' || ch == '!' || ch == '#' || ch == '[');
+}
+
+static inline bool IsPhpWordStart(const unsigned char ch) {
+ return (isascii(ch) && (isalpha(ch) || (ch == '_'))) || (ch >= 0x7f);
+}
+
+static inline bool IsPhpWordChar(char ch) {
+ return IsADigit(ch) || IsPhpWordStart(ch);
+}
+
+static bool InTagState(int state) {
+ return state == SCE_H_TAG || state == SCE_H_TAGUNKNOWN ||
+ state == SCE_H_SCRIPT ||
+ state == SCE_H_ATTRIBUTE || state == SCE_H_ATTRIBUTEUNKNOWN ||
+ state == SCE_H_NUMBER || state == SCE_H_OTHER ||
+ state == SCE_H_DOUBLESTRING || state == SCE_H_SINGLESTRING;
+}
+
+static bool IsCommentState(const int state) {
+ return state == SCE_H_COMMENT || state == SCE_H_SGML_COMMENT;
+}
+
+static bool IsScriptCommentState(const int state) {
+ return state == SCE_HJ_COMMENT || state == SCE_HJ_COMMENTLINE || state == SCE_HJA_COMMENT ||
+ state == SCE_HJA_COMMENTLINE || state == SCE_HB_COMMENTLINE || state == SCE_HBA_COMMENTLINE;
+}
+
+static bool isLineEnd(char ch) {
+ return ch == '\r' || ch == '\n';
+}
+
+static bool isOKBeforeRE(char ch) {
+ return (ch == '(') || (ch == '=') || (ch == ',');
+}
+
+static bool isPHPStringState(int state) {
+ return
+ (state == SCE_HPHP_HSTRING) ||
+ (state == SCE_HPHP_SIMPLESTRING) ||
+ (state == SCE_HPHP_HSTRING_VARIABLE) ||
+ (state == SCE_HPHP_COMPLEX_VARIABLE);
+}
+
+static int FindPhpStringDelimiter(char *phpStringDelimiter, const int phpStringDelimiterSize, int i, const int lengthDoc, Accessor &styler) {
+ int j;
+ while (i < lengthDoc && (styler[i] == ' ' || styler[i] == '\t'))
+ i++;
+ phpStringDelimiter[0] = '\n';
+ for (j = i; j < lengthDoc && styler[j] != '\n' && styler[j] != '\r'; j++) {
+ if (j - i < phpStringDelimiterSize - 2)
+ phpStringDelimiter[j-i+1] = styler[j];
+ else
+ i++;
+ }
+ phpStringDelimiter[j-i+1] = '\0';
+ return j;
+}
+
+static void ColouriseHyperTextDoc(unsigned int startPos, int 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]; // SGML (DTD) keywords
+
+ // Lexer for HTML requires more lexical states (7 bits worth) than most lexers
+ styler.StartAt(startPos, STYLE_MAX);
+ char prevWord[200];
+ prevWord[0] = '\0';
+ char phpStringDelimiter[200]; // PHP is not limited in length, we are
+ phpStringDelimiter[0] = '\0';
+ int StateToPrint = initStyle;
+ int state = stateForPrintState(StateToPrint);
+
+ // If inside a tag, it may be a script tag, so reread from the start to ensure any language tags are seen
+ if (InTagState(state)) {
+ while ((startPos > 0) && (InTagState(styler.StyleAt(startPos - 1)))) {
+ startPos--;
+ length++;
+ }
+ state = SCE_H_DEFAULT;
+ }
+ // String can be heredoc, must find a delimiter first
+ while (startPos > 0 && isPHPStringState(state) && state != SCE_HPHP_SIMPLESTRING) {
+ startPos--;
+ length++;
+ state = styler.StyleAt(startPos);
+ }
+ styler.StartAt(startPos, STYLE_MAX);
+
+ int lineCurrent = styler.GetLine(startPos);
+ int lineState;
+ if (lineCurrent > 0) {
+ lineState = styler.GetLineState(lineCurrent);
+ } else {
+ // Default client and ASP scripting language is JavaScript
+ lineState = eScriptJS << 8;
+ lineState |= styler.GetPropertyInt("asp.default.language", eScriptJS) << 4;
+ }
+ script_mode inScriptType = script_mode((lineState >> 0) & 0x03); // 2 bits of scripting mode
+ bool tagOpened = (lineState >> 2) & 0x01; // 1 bit to know if we are in an opened tag
+ bool tagClosing = (lineState >> 3) & 0x01; // 1 bit to know if we are in a closing tag
+ bool tagDontFold = false; //some HTML tags should not be folded
+ script_type aspScript = script_type((lineState >> 4) & 0x0F); // 4 bits of script name
+ script_type clientScript = script_type((lineState >> 8) & 0x0F); // 4 bits of script name
+ int beforePreProc = (lineState >> 12) & 0xFF; // 8 bits of state
+
+ script_type scriptLanguage = ScriptOfState(state);
+
+ const bool foldHTML = styler.GetPropertyInt("fold.html", 0) != 0;
+ const bool fold = foldHTML && styler.GetPropertyInt("fold", 0);
+ const bool foldHTMLPreprocessor = foldHTML && styler.GetPropertyInt("fold.html.preprocessor", 1);
+ const bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ const bool caseSensitive = styler.GetPropertyInt("html.tags.case.sensitive", 0) != 0;
+
+ int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
+ int levelCurrent = levelPrev;
+ int visibleChars = 0;
+
+ char chPrev = ' ';
+ char ch = ' ';
+ char chPrevNonWhite = ' ';
+ // look back to set chPrevNonWhite properly for better regex colouring
+ if (scriptLanguage == eScriptJS && startPos > 0) {
+ int back = startPos;
+ int style = 0;
+ while (--back) {
+ style = styler.StyleAt(back);
+ if (style < SCE_HJ_DEFAULT || style > SCE_HJ_COMMENTDOC)
+ // includes SCE_HJ_COMMENT & SCE_HJ_COMMENTLINE
+ break;
+ }
+ if (style == SCE_HJ_SYMBOLS) {
+ chPrevNonWhite = styler.SafeGetCharAt(back);
+ }
+ }
+
+ styler.StartSegment(startPos);
+ const int lengthDoc = startPos + length;
+ for (int i = startPos; i < lengthDoc; i++) {
+ const char chPrev2 = chPrev;
+ chPrev = ch;
+ if (!isspacechar(ch) && state != SCE_HJ_COMMENT &&
+ state != SCE_HJ_COMMENTLINE && state != SCE_HJ_COMMENTDOC)
+ chPrevNonWhite = ch;
+ ch = styler[i];
+ char chNext = styler.SafeGetCharAt(i + 1);
+ const char chNext2 = styler.SafeGetCharAt(i + 2);
+
+ // Handle DBCS codepages
+ if (styler.IsLeadByte(ch)) {
+ chPrev = ' ';
+ i += 1;
+ continue;
+ }
+
+ if ((!isspacechar(ch) || !foldCompact) && fold)
+ visibleChars++;
+
+ // decide what is the current state to print (depending of the script tag)
+ StateToPrint = statePrintForState(state, inScriptType);
+
+ // handle script folding
+ if (fold) {
+ switch (scriptLanguage) {
+ case eScriptJS:
+ case eScriptPHP:
+ //not currently supported case eScriptVBS:
+
+ if ((state != SCE_HPHP_COMMENT) && (state != SCE_HPHP_COMMENTLINE) && (state != SCE_HJ_COMMENT) && (state != SCE_HJ_COMMENTLINE) && (state != SCE_HJ_COMMENTDOC) && (!isStringState(state))) {
+ //Platform::DebugPrintf("state=%d, StateToPrint=%d, initStyle=%d\n", state, StateToPrint, initStyle);
+ //if ((state == SCE_HPHP_OPERATOR) || (state == SCE_HPHP_DEFAULT) || (state == SCE_HJ_SYMBOLS) || (state == SCE_HJ_START) || (state == SCE_HJ_DEFAULT)) {
+ if ((ch == '{') || (ch == '}')) {
+ levelCurrent += (ch == '{') ? 1 : -1;
+ }
+ }
+ break;
+ case eScriptPython:
+ if (state != SCE_HP_COMMENTLINE) {
+ if ((ch == ':') && ((chNext == '\n') || (chNext == '\r' && chNext2 == '\n'))) {
+ levelCurrent++;
+ } else if ((ch == '\n') && !((chNext == '\r') && (chNext2 == '\n')) && (chNext != '\n')) {
+ // check if the number of tabs is lower than the level
+ int Findlevel = (levelCurrent & ~SC_FOLDLEVELBASE) * 8;
+ for (int j = 0; Findlevel > 0; j++) {
+ char chTmp = styler.SafeGetCharAt(i + j + 1);
+ if (chTmp == '\t') {
+ Findlevel -= 8;
+ } else if (chTmp == ' ') {
+ Findlevel--;
+ } else {
+ break;
+ }
+ }
+
+ if (Findlevel > 0) {
+ levelCurrent -= Findlevel / 8;
+ if (Findlevel % 8)
+ levelCurrent--;
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ((ch == '\r' && chNext != '\n') || (ch == '\n')) {
+ // Trigger on CR only (Mac style) or either on LF from CR+LF (Dos/Win) or on LF alone (Unix)
+ // Avoid triggering two times on Dos/Win
+ // New line -> record any line state onto /next/ line
+ if (fold) {
+ int lev = levelPrev;
+ if (visibleChars == 0)
+ lev |= SC_FOLDLEVELWHITEFLAG;
+ if ((levelCurrent > levelPrev) && (visibleChars > 0))
+ lev |= SC_FOLDLEVELHEADERFLAG;
+
+ styler.SetLevel(lineCurrent, lev);
+ visibleChars = 0;
+ levelPrev = levelCurrent;
+ }
+ lineCurrent++;
+ styler.SetLineState(lineCurrent,
+ ((inScriptType & 0x03) << 0) |
+ ((tagOpened & 0x01) << 2) |
+ ((tagClosing & 0x01) << 3) |
+ ((aspScript & 0x0F) << 4) |
+ ((clientScript & 0x0F) << 8) |
+ ((beforePreProc & 0xFF) << 12));
+ }
+
+ // generic end of script processing
+ else if ((inScriptType == eNonHtmlScript) && (ch == '<') && (chNext == '/')) {
+ // Check if it's the end of the script tag (or any other HTML tag)
+ switch (state) {
+ // in these cases, you can embed HTML tags (to confirm !!!!!!!!!!!!!!!!!!!!!!)
+ case SCE_H_DOUBLESTRING:
+ case SCE_H_SINGLESTRING:
+ case SCE_HJ_COMMENT:
+ case SCE_HJ_COMMENTDOC:
+ //case SCE_HJ_COMMENTLINE: // removed as this is a common thing done to hide
+ // the end of script marker from some JS interpreters.
+ case SCE_HJ_DOUBLESTRING:
+ case SCE_HJ_SINGLESTRING:
+ case SCE_HJ_REGEX:
+ case SCE_HB_STRING:
+ case SCE_HP_STRING:
+ case SCE_HP_TRIPLE:
+ case SCE_HP_TRIPLEDOUBLE:
+ break;
+ default :
+ // check if the closing tag is a script tag
+ if (state == SCE_HJ_COMMENTLINE) {
+ char tag[7]; // room for the <script> tag
+ char chr; // current char
+ int j=0;
+ chr = styler.SafeGetCharAt(i+2);
+ while (j < 6 && !isspacechar(chr)) {
+ tag[j++] = static_cast<char>(MakeLowerCase(chr));
+ chr = styler.SafeGetCharAt(i+2+j);
+ }
+ tag[j] = '\0';
+ if (strcmp(tag, "script") != 0) break;
+ }
+ // closing tag of the script (it's a closing HTML tag anyway)
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_H_TAGUNKNOWN;
+ inScriptType = eHtml;
+ scriptLanguage = eScriptNone;
+ clientScript = eScriptJS;
+ i += 2;
+ visibleChars += 2;
+ tagClosing = true;
+ continue;
+ }
+ }
+
+ /////////////////////////////////////
+ // handle the start of PHP pre-processor = Non-HTML
+ else if ((state != SCE_H_ASPAT) &&
+ !isPHPStringState(state) &&
+ (state != SCE_HPHP_COMMENT) &&
+ (ch == '<') &&
+ (chNext == '?') &&
+ !IsScriptCommentState(state) ) {
+ scriptLanguage = segIsScriptingIndicator(styler, i + 2, i + 10, eScriptPHP);
+ if (scriptLanguage != eScriptPHP && isStringState(state)) continue;
+ styler.ColourTo(i - 1, StateToPrint);
+ beforePreProc = state;
+ i++;
+ visibleChars++;
+ i += PrintScriptingIndicatorOffset(styler, styler.GetStartSegment() + 2, i + 10);
+ if (scriptLanguage == eScriptXML)
+ styler.ColourTo(i, SCE_H_XMLSTART);
+ else
+ styler.ColourTo(i, SCE_H_QUESTION);
+ state = StateForScript(scriptLanguage);
+ if (inScriptType == eNonHtmlScript)
+ inScriptType = eNonHtmlScriptPreProc;
+ else
+ inScriptType = eNonHtmlPreProc;
+ // Fold whole script, but not if the XML first tag (all XML-like tags in this case)
+ if (foldHTMLPreprocessor && (scriptLanguage != eScriptXML)) {
+ levelCurrent++;
+ }
+ // should be better
+ ch = styler.SafeGetCharAt(i);
+ continue;
+ }
+
+ // handle the start of ASP pre-processor = Non-HTML
+ else if (!isCommentASPState(state) && (ch == '<') && (chNext == '%') && !isPHPStringState(state)) {
+ styler.ColourTo(i - 1, StateToPrint);
+ beforePreProc = state;
+ if (inScriptType == eNonHtmlScript)
+ inScriptType = eNonHtmlScriptPreProc;
+ else
+ inScriptType = eNonHtmlPreProc;
+
+ if (chNext2 == '@') {
+ i += 2; // place as if it was the second next char treated
+ visibleChars += 2;
+ state = SCE_H_ASPAT;
+ } else if ((chNext2 == '-') && (styler.SafeGetCharAt(i + 3) == '-')) {
+ styler.ColourTo(i + 3, SCE_H_ASP);
+ state = SCE_H_XCCOMMENT;
+ scriptLanguage = eScriptVBS;
+ continue;
+ } else {
+ if (chNext2 == '=') {
+ i += 2; // place as if it was the second next char treated
+ visibleChars += 2;
+ } else {
+ i++; // place as if it was the next char treated
+ visibleChars++;
+ }
+
+ state = StateForScript(aspScript);
+ }
+ scriptLanguage = eScriptVBS;
+ styler.ColourTo(i, SCE_H_ASP);
+ // fold whole script
+ if (foldHTMLPreprocessor)
+ levelCurrent++;
+ // should be better
+ ch = styler.SafeGetCharAt(i);
+ continue;
+ }
+
+ /////////////////////////////////////
+ // handle the start of SGML language (DTD)
+ else if (((scriptLanguage == eScriptNone) || (scriptLanguage == eScriptXML)) &&
+ (chPrev == '<') &&
+ (ch == '!') &&
+ (StateToPrint != SCE_H_CDATA) &&
+ (!IsCommentState(StateToPrint)) &&
+ (!IsScriptCommentState(StateToPrint)) ) {
+ beforePreProc = state;
+ styler.ColourTo(i - 2, StateToPrint);
+ if ((chNext == '-') && (chNext2 == '-')) {
+ state = SCE_H_COMMENT; // wait for a pending command
+ styler.ColourTo(i + 2, SCE_H_COMMENT);
+ i += 2; // follow styling after the --
+ } else if (isWordCdata(i + 1, i + 7, styler)) {
+ state = SCE_H_CDATA;
+ } else {
+ styler.ColourTo(i, SCE_H_SGML_DEFAULT); // <! is default
+ scriptLanguage = eScriptSGML;
+ state = SCE_H_SGML_COMMAND; // wait for a pending command
+ }
+ // fold whole tag (-- when closing the tag)
+ if (foldHTMLPreprocessor)
+ levelCurrent++;
+ continue;
+ }
+
+ // handle the end of a pre-processor = Non-HTML
+ else if ((
+ ((inScriptType == eNonHtmlPreProc)
+ || (inScriptType == eNonHtmlScriptPreProc)) && (
+ ((scriptLanguage != eScriptNone) && stateAllowsTermination(state) && ((ch == '%') || (ch == '?')))
+ ) && (chNext == '>')) ||
+ ((scriptLanguage == eScriptSGML) && (ch == '>') && (state != SCE_H_SGML_COMMENT))) {
+ if (state == SCE_H_ASPAT) {
+ aspScript = segIsScriptingIndicator(styler,
+ styler.GetStartSegment(), i - 1, aspScript);
+ }
+ // Bounce out of any ASP mode
+ switch (state) {
+ case SCE_HJ_WORD:
+ classifyWordHTJS(styler.GetStartSegment(), i - 1, keywords2, styler, inScriptType);
+ break;
+ case SCE_HB_WORD:
+ classifyWordHTVB(styler.GetStartSegment(), i - 1, keywords3, styler, inScriptType);
+ break;
+ case SCE_HP_WORD:
+ classifyWordHTPy(styler.GetStartSegment(), i - 1, keywords4, styler, prevWord, inScriptType);
+ break;
+ case SCE_HPHP_WORD:
+ classifyWordHTPHP(styler.GetStartSegment(), i - 1, keywords5, styler);
+ break;
+ case SCE_H_XCCOMMENT:
+ styler.ColourTo(i - 1, state);
+ break;
+ default :
+ styler.ColourTo(i - 1, StateToPrint);
+ break;
+ }
+ if (scriptLanguage != eScriptSGML) {
+ i++;
+ visibleChars++;
+ }
+ if (ch == '%')
+ styler.ColourTo(i, SCE_H_ASP);
+ else if (scriptLanguage == eScriptXML)
+ styler.ColourTo(i, SCE_H_XMLEND);
+ else if (scriptLanguage == eScriptSGML)
+ styler.ColourTo(i, SCE_H_SGML_DEFAULT);
+ else
+ styler.ColourTo(i, SCE_H_QUESTION);
+ state = beforePreProc;
+ if (inScriptType == eNonHtmlScriptPreProc)
+ inScriptType = eNonHtmlScript;
+ else
+ inScriptType = eHtml;
+ // Unfold all scripting languages, except for XML tag
+ if (foldHTMLPreprocessor && (scriptLanguage != eScriptXML)) {
+ levelCurrent--;
+ }
+ scriptLanguage = eScriptNone;
+ continue;
+ }
+ /////////////////////////////////////
+
+ switch (state) {
+ case SCE_H_DEFAULT:
+ if (ch == '<') {
+ // in HTML, fold on tag open and unfold on tag close
+ tagOpened = true;
+ tagClosing = (chNext == '/');
+ styler.ColourTo(i - 1, StateToPrint);
+ if (chNext != '!')
+ state = SCE_H_TAGUNKNOWN;
+ } else if (ch == '&') {
+ styler.ColourTo(i - 1, SCE_H_DEFAULT);
+ state = SCE_H_ENTITY;
+ }
+ break;
+ case SCE_H_SGML_DEFAULT:
+ case SCE_H_SGML_BLOCK_DEFAULT:
+// if (scriptLanguage == eScriptSGMLblock)
+// StateToPrint = SCE_H_SGML_BLOCK_DEFAULT;
+
+ if (ch == '\"') {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_H_SGML_DOUBLESTRING;
+ } else if (ch == '\'') {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_H_SGML_SIMPLESTRING;
+ } else if ((ch == '-') && (chPrev == '-')) {
+ styler.ColourTo(i - 2, StateToPrint);
+ state = SCE_H_SGML_COMMENT;
+ } else if (isascii(ch) && isalpha(ch) && (chPrev == '%')) {
+ styler.ColourTo(i - 2, StateToPrint);
+ state = SCE_H_SGML_ENTITY;
+ } else if (ch == '#') {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_H_SGML_SPECIAL;
+ } else if (ch == '[') {
+ styler.ColourTo(i - 1, StateToPrint);
+ scriptLanguage = eScriptSGMLblock;
+ state = SCE_H_SGML_BLOCK_DEFAULT;
+ } else if (ch == ']') {
+ if (scriptLanguage == eScriptSGMLblock) {
+ styler.ColourTo(i, StateToPrint);
+ scriptLanguage = eScriptSGML;
+ } else {
+ styler.ColourTo(i - 1, StateToPrint);
+ styler.ColourTo(i, SCE_H_SGML_ERROR);
+ }
+ state = SCE_H_SGML_DEFAULT;
+ } else if (scriptLanguage == eScriptSGMLblock) {
+ if ((ch == '!') && (chPrev == '<')) {
+ styler.ColourTo(i - 2, StateToPrint);
+ styler.ColourTo(i, SCE_H_SGML_DEFAULT);
+ state = SCE_H_SGML_COMMAND;
+ } else if (ch == '>') {
+ styler.ColourTo(i - 1, StateToPrint);
+ styler.ColourTo(i, SCE_H_SGML_DEFAULT);
+ }
+ }
+ break;
+ case SCE_H_SGML_COMMAND:
+ if ((ch == '-') && (chPrev == '-')) {
+ styler.ColourTo(i - 2, StateToPrint);
+ state = SCE_H_SGML_COMMENT;
+ } else if (!issgmlwordchar(ch)) {
+ if (isWordHSGML(styler.GetStartSegment(), i - 1, keywords6, styler)) {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_H_SGML_1ST_PARAM;
+ } else {
+ state = SCE_H_SGML_ERROR;
+ }
+ }
+ break;
+ case SCE_H_SGML_1ST_PARAM:
+ // wait for the beginning of the word
+ if ((ch == '-') && (chPrev == '-')) {
+ if (scriptLanguage == eScriptSGMLblock) {
+ styler.ColourTo(i - 2, SCE_H_SGML_BLOCK_DEFAULT);
+ } else {
+ styler.ColourTo(i - 2, SCE_H_SGML_DEFAULT);
+ }
+ state = SCE_H_SGML_1ST_PARAM_COMMENT;
+ } else if (issgmlwordchar(ch)) {
+ if (scriptLanguage == eScriptSGMLblock) {
+ styler.ColourTo(i - 1, SCE_H_SGML_BLOCK_DEFAULT);
+ } else {
+ styler.ColourTo(i - 1, SCE_H_SGML_DEFAULT);
+ }
+ // find the length of the word
+ int size = 1;
+ while (ishtmlwordchar(styler.SafeGetCharAt(i + size)))
+ size++;
+ styler.ColourTo(i + size - 1, StateToPrint);
+ i += size - 1;
+ visibleChars += size - 1;
+ ch = styler.SafeGetCharAt(i);
+ if (scriptLanguage == eScriptSGMLblock) {
+ state = SCE_H_SGML_BLOCK_DEFAULT;
+ } else {
+ state = SCE_H_SGML_DEFAULT;
+ }
+ continue;
+ }
+ break;
+ case SCE_H_SGML_ERROR:
+ if ((ch == '-') && (chPrev == '-')) {
+ styler.ColourTo(i - 2, StateToPrint);
+ state = SCE_H_SGML_COMMENT;
+ }
+ case SCE_H_SGML_DOUBLESTRING:
+ if (ch == '\"') {
+ styler.ColourTo(i, StateToPrint);
+ state = SCE_H_SGML_DEFAULT;
+ }
+ break;
+ case SCE_H_SGML_SIMPLESTRING:
+ if (ch == '\'') {
+ styler.ColourTo(i, StateToPrint);
+ state = SCE_H_SGML_DEFAULT;
+ }
+ break;
+ case SCE_H_SGML_COMMENT:
+ if ((ch == '-') && (chPrev == '-')) {
+ styler.ColourTo(i, StateToPrint);
+ state = SCE_H_SGML_DEFAULT;
+ }
+ break;
+ case SCE_H_CDATA:
+ if ((chPrev2 == ']') && (chPrev == ']') && (ch == '>')) {
+ styler.ColourTo(i, StateToPrint);
+ state = SCE_H_DEFAULT;
+ levelCurrent--;
+ }
+ break;
+ case SCE_H_COMMENT:
+ if ((chPrev2 == '-') && (chPrev == '-') && (ch == '>')) {
+ styler.ColourTo(i, StateToPrint);
+ state = SCE_H_DEFAULT;
+ levelCurrent--;
+ }
+ break;
+ case SCE_H_SGML_1ST_PARAM_COMMENT:
+ if ((ch == '-') && (chPrev == '-')) {
+ styler.ColourTo(i, SCE_H_SGML_COMMENT);
+ state = SCE_H_SGML_1ST_PARAM;
+ }
+ break;
+ case SCE_H_SGML_SPECIAL:
+ if (!(isascii(ch) && isupper(ch))) {
+ styler.ColourTo(i - 1, StateToPrint);
+ if (isalnum(ch)) {
+ state = SCE_H_SGML_ERROR;
+ } else {
+ state = SCE_H_SGML_DEFAULT;
+ }
+ }
+ break;
+ case SCE_H_SGML_ENTITY:
+ if (ch == ';') {
+ styler.ColourTo(i, StateToPrint);
+ state = SCE_H_SGML_DEFAULT;
+ } else if (!(isascii(ch) && isalnum(ch)) && ch != '-' && ch != '.') {
+ styler.ColourTo(i, SCE_H_SGML_ERROR);
+ state = SCE_H_SGML_DEFAULT;
+ }
+ break;
+ case SCE_H_ENTITY:
+ if (ch == ';') {
+ styler.ColourTo(i, StateToPrint);
+ state = SCE_H_DEFAULT;
+ }
+ if (ch != '#' && !(isascii(ch) && isalnum(ch)) // Should check that '#' follows '&', but it is unlikely anyway...
+ && ch != '.' && ch != '-' && ch != '_' && ch != ':') { // valid in XML
+ styler.ColourTo(i, SCE_H_TAGUNKNOWN);
+ state = SCE_H_DEFAULT;
+ }
+ break;
+ case SCE_H_TAGUNKNOWN:
+ if (!ishtmlwordchar(ch) && !((ch == '/') && (chPrev == '<')) && ch != '[') {
+ int eClass = classifyTagHTML(styler.GetStartSegment(),
+ i - 1, keywords, styler, tagDontFold, caseSensitive);
+ if (eClass == SCE_H_SCRIPT) {
+ if (!tagClosing) {
+ inScriptType = eNonHtmlScript;
+ scriptLanguage = clientScript;
+ eClass = SCE_H_TAG;
+ } else {
+ scriptLanguage = eScriptNone;
+ eClass = SCE_H_TAG;
+ }
+ }
+ if (ch == '>') {
+ styler.ColourTo(i, eClass);
+ if (inScriptType == eNonHtmlScript) {
+ state = StateForScript(scriptLanguage);
+ } else {
+ state = SCE_H_DEFAULT;
+ }
+ tagOpened = false;
+ if (!tagDontFold){
+ if (tagClosing) {
+ levelCurrent--;
+ } else {
+ levelCurrent++;
+ }
+ }
+ tagClosing = false;
+ } else if (ch == '/' && chNext == '>') {
+ if (eClass == SCE_H_TAGUNKNOWN) {
+ styler.ColourTo(i + 1, SCE_H_TAGUNKNOWN);
+ } else {
+ styler.ColourTo(i - 1, StateToPrint);
+ styler.ColourTo(i + 1, SCE_H_TAGEND);
+ }
+ i++;
+ ch = chNext;
+ state = SCE_H_DEFAULT;
+ tagOpened = false;
+ } else {
+ if (eClass != SCE_H_TAGUNKNOWN) {
+ if (eClass == SCE_H_SGML_DEFAULT) {
+ state = SCE_H_SGML_DEFAULT;
+ } else {
+ state = SCE_H_OTHER;
+ }
+ }
+ }
+ }
+ break;
+ case SCE_H_ATTRIBUTE:
+ if (!ishtmlwordchar(ch) && ch != '/' && ch != '-') {
+ if (inScriptType == eNonHtmlScript) {
+ int scriptLanguagePrev = scriptLanguage;
+ clientScript = segIsScriptingIndicator(styler, styler.GetStartSegment(), i - 1, scriptLanguage);
+ scriptLanguage = clientScript;
+ if ((scriptLanguagePrev != scriptLanguage) && (scriptLanguage == eScriptNone))
+ inScriptType = eHtml;
+ }
+ classifyAttribHTML(styler.GetStartSegment(), i - 1, keywords, styler);
+ if (ch == '>') {
+ styler.ColourTo(i, SCE_H_TAG);
+ if (inScriptType == eNonHtmlScript) {
+ state = StateForScript(scriptLanguage);
+ } else {
+ state = SCE_H_DEFAULT;
+ }
+ tagOpened = false;
+ if (!tagDontFold){
+ if (tagClosing){
+ levelCurrent--;
+ } else {
+ levelCurrent++;
+ }
+ }
+ tagClosing = false;
+ } else if (ch == '=') {
+ styler.ColourTo(i, SCE_H_OTHER);
+ state = SCE_H_VALUE;
+ } else {
+ state = SCE_H_OTHER;
+ }
+ }
+ break;
+ case SCE_H_OTHER:
+ if (ch == '>') {
+ styler.ColourTo(i - 1, StateToPrint);
+ styler.ColourTo(i, SCE_H_TAG);
+ if (inScriptType == eNonHtmlScript) {
+ state = StateForScript(scriptLanguage);
+ } else {
+ state = SCE_H_DEFAULT;
+ }
+ tagOpened = false;
+ if (!tagDontFold){
+ if (tagClosing){
+ levelCurrent--;
+ } else {
+ levelCurrent++;
+ }
+ }
+ tagClosing = false;
+ } else if (ch == '\"') {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_H_DOUBLESTRING;
+ } else if (ch == '\'') {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_H_SINGLESTRING;
+ } else if (ch == '=') {
+ styler.ColourTo(i, StateToPrint);
+ state = SCE_H_VALUE;
+ } else if (ch == '/' && chNext == '>') {
+ styler.ColourTo(i - 1, StateToPrint);
+ styler.ColourTo(i + 1, SCE_H_TAGEND);
+ i++;
+ ch = chNext;
+ state = SCE_H_DEFAULT;
+ tagOpened = false;
+ } else if (ch == '?' && chNext == '>') {
+ styler.ColourTo(i - 1, StateToPrint);
+ styler.ColourTo(i + 1, SCE_H_XMLEND);
+ i++;
+ ch = chNext;
+ state = SCE_H_DEFAULT;
+ } else if (ishtmlwordchar(ch)) {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_H_ATTRIBUTE;
+ }
+ break;
+ case SCE_H_DOUBLESTRING:
+ if (ch == '\"') {
+ if (inScriptType == eNonHtmlScript) {
+ scriptLanguage = segIsScriptingIndicator(styler, styler.GetStartSegment(), i, scriptLanguage);
+ }
+ styler.ColourTo(i, SCE_H_DOUBLESTRING);
+ state = SCE_H_OTHER;
+ }
+ break;
+ case SCE_H_SINGLESTRING:
+ if (ch == '\'') {
+ if (inScriptType == eNonHtmlScript) {
+ scriptLanguage = segIsScriptingIndicator(styler, styler.GetStartSegment(), i, scriptLanguage);
+ }
+ styler.ColourTo(i, SCE_H_SINGLESTRING);
+ state = SCE_H_OTHER;
+ }
+ break;
+ case SCE_H_VALUE:
+ if (!ishtmlwordchar(ch)) {
+ if (ch == '\"' && chPrev == '=') {
+ // Should really test for being first character
+ state = SCE_H_DOUBLESTRING;
+ } else if (ch == '\'' && chPrev == '=') {
+ state = SCE_H_SINGLESTRING;
+ } else {
+ if (IsNumber(styler.GetStartSegment(), styler)) {
+ styler.ColourTo(i - 1, SCE_H_NUMBER);
+ } else {
+ styler.ColourTo(i - 1, StateToPrint);
+ }
+ if (ch == '>') {
+ styler.ColourTo(i, SCE_H_TAG);
+ if (inScriptType == eNonHtmlScript) {
+ state = StateForScript(scriptLanguage);
+ } else {
+ state = SCE_H_DEFAULT;
+ }
+ tagOpened = false;
+ if (!tagDontFold){
+ if (tagClosing){
+ levelCurrent--;
+ } else {
+ levelCurrent++;
+ }
+ }
+ tagClosing = false;
+ } else {
+ state = SCE_H_OTHER;
+ }
+ }
+ }
+ break;
+ case SCE_HJ_DEFAULT:
+ case SCE_HJ_START:
+ case SCE_HJ_SYMBOLS:
+ if (iswordstart(ch)) {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HJ_WORD;
+ } else if (ch == '/' && chNext == '*') {
+ styler.ColourTo(i - 1, StateToPrint);
+ if (chNext2 == '*')
+ state = SCE_HJ_COMMENTDOC;
+ else
+ state = SCE_HJ_COMMENT;
+ } else if (ch == '/' && chNext == '/') {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HJ_COMMENTLINE;
+ } else if (ch == '/' && isOKBeforeRE(chPrevNonWhite)) {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HJ_REGEX;
+ } else if (ch == '\"') {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HJ_DOUBLESTRING;
+ } else if (ch == '\'') {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HJ_SINGLESTRING;
+ } else if ((ch == '<') && (chNext == '!') && (chNext2 == '-') &&
+ styler.SafeGetCharAt(i + 3) == '-') {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HJ_COMMENTLINE;
+ } else if ((ch == '-') && (chNext == '-') && (chNext2 == '>')) {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HJ_COMMENTLINE;
+ i += 2;
+ } else if (isoperator(ch)) {
+ styler.ColourTo(i - 1, StateToPrint);
+ styler.ColourTo(i, statePrintForState(SCE_HJ_SYMBOLS, inScriptType));
+ state = SCE_HJ_DEFAULT;
+ } else if ((ch == ' ') || (ch == '\t')) {
+ if (state == SCE_HJ_START) {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HJ_DEFAULT;
+ }
+ }
+ break;
+ case SCE_HJ_WORD:
+ if (!iswordchar(ch)) {
+ classifyWordHTJS(styler.GetStartSegment(), i - 1, keywords2, styler, inScriptType);
+ //styler.ColourTo(i - 1, eHTJSKeyword);
+ state = SCE_HJ_DEFAULT;
+ if (ch == '/' && chNext == '*') {
+ if (chNext2 == '*')
+ state = SCE_HJ_COMMENTDOC;
+ else
+ state = SCE_HJ_COMMENT;
+ } else if (ch == '/' && chNext == '/') {
+ state = SCE_HJ_COMMENTLINE;
+ } else if (ch == '\"') {
+ state = SCE_HJ_DOUBLESTRING;
+ } else if (ch == '\'') {
+ state = SCE_HJ_SINGLESTRING;
+ } else if ((ch == '-') && (chNext == '-') && (chNext2 == '>')) {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HJ_COMMENTLINE;
+ i += 2;
+ } else if (isoperator(ch)) {
+ styler.ColourTo(i, statePrintForState(SCE_HJ_SYMBOLS, inScriptType));
+ state = SCE_HJ_DEFAULT;
+ }
+ }
+ break;
+ case SCE_HJ_COMMENT:
+ case SCE_HJ_COMMENTDOC:
+ if (ch == '/' && chPrev == '*') {
+ styler.ColourTo(i, StateToPrint);
+ state = SCE_HJ_DEFAULT;
+ ch = ' ';
+ }
+ break;
+ case SCE_HJ_COMMENTLINE:
+ if (ch == '\r' || ch == '\n') {
+ styler.ColourTo(i - 1, statePrintForState(SCE_HJ_COMMENTLINE, inScriptType));
+ state = SCE_HJ_DEFAULT;
+ ch = ' ';
+ }
+ break;
+ case SCE_HJ_DOUBLESTRING:
+ if (ch == '\\') {
+ if (chNext == '\"' || chNext == '\'' || chNext == '\\') {
+ i++;
+ }
+ } else if (ch == '\"') {
+ styler.ColourTo(i, statePrintForState(SCE_HJ_DOUBLESTRING, inScriptType));
+ state = SCE_HJ_DEFAULT;
+ } else if ((inScriptType == eNonHtmlScript) && (ch == '-') && (chNext == '-') && (chNext2 == '>')) {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HJ_COMMENTLINE;
+ i += 2;
+ } else if (isLineEnd(ch)) {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HJ_STRINGEOL;
+ }
+ break;
+ case SCE_HJ_SINGLESTRING:
+ if (ch == '\\') {
+ if (chNext == '\"' || chNext == '\'' || chNext == '\\') {
+ i++;
+ }
+ } else if (ch == '\'') {
+ styler.ColourTo(i, statePrintForState(SCE_HJ_SINGLESTRING, inScriptType));
+ state = SCE_HJ_DEFAULT;
+ } else if ((inScriptType == eNonHtmlScript) && (ch == '-') && (chNext == '-') && (chNext2 == '>')) {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HJ_COMMENTLINE;
+ i += 2;
+ } else if (isLineEnd(ch)) {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HJ_STRINGEOL;
+ }
+ break;
+ case SCE_HJ_STRINGEOL:
+ if (!isLineEnd(ch)) {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HJ_DEFAULT;
+ } else if (!isLineEnd(chNext)) {
+ styler.ColourTo(i, StateToPrint);
+ state = SCE_HJ_DEFAULT;
+ }
+ break;
+ case SCE_HJ_REGEX:
+ if (ch == '\r' || ch == '\n' || ch == '/') {
+ if (ch == '/') {
+ while (isascii(chNext) && islower(chNext)) { // gobble regex flags
+ i++;
+ ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ }
+ styler.ColourTo(i, StateToPrint);
+ state = SCE_HJ_DEFAULT;
+ } else if (ch == '\\') {
+ // Gobble up the quoted character
+ if (chNext == '\\' || chNext == '/') {
+ i++;
+ ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ }
+ break;
+ case SCE_HB_DEFAULT:
+ case SCE_HB_START:
+ if (iswordstart(ch)) {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HB_WORD;
+ } else if (ch == '\'') {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HB_COMMENTLINE;
+ } else if (ch == '\"') {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HB_STRING;
+ } else if ((ch == '<') && (chNext == '!') && (chNext2 == '-') &&
+ styler.SafeGetCharAt(i + 3) == '-') {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HB_COMMENTLINE;
+ } else if (isoperator(ch)) {
+ styler.ColourTo(i - 1, StateToPrint);
+ styler.ColourTo(i, statePrintForState(SCE_HB_DEFAULT, inScriptType));
+ state = SCE_HB_DEFAULT;
+ } else if ((ch == ' ') || (ch == '\t')) {
+ if (state == SCE_HB_START) {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HB_DEFAULT;
+ }
+ }
+ break;
+ case SCE_HB_WORD:
+ if (!iswordchar(ch)) {
+ state = classifyWordHTVB(styler.GetStartSegment(), i - 1, keywords3, styler, inScriptType);
+ if (state == SCE_HB_DEFAULT) {
+ if (ch == '\"') {
+ state = SCE_HB_STRING;
+ } else if (ch == '\'') {
+ state = SCE_HB_COMMENTLINE;
+ } else if (isoperator(ch)) {
+ styler.ColourTo(i, statePrintForState(SCE_HB_DEFAULT, inScriptType));
+ state = SCE_HB_DEFAULT;
+ }
+ }
+ }
+ break;
+ case SCE_HB_STRING:
+ if (ch == '\"') {
+ styler.ColourTo(i, StateToPrint);
+ state = SCE_HB_DEFAULT;
+ } else if (ch == '\r' || ch == '\n') {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HB_STRINGEOL;
+ }
+ break;
+ case SCE_HB_COMMENTLINE:
+ if (ch == '\r' || ch == '\n') {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HB_DEFAULT;
+ }
+ break;
+ case SCE_HB_STRINGEOL:
+ if (!isLineEnd(ch)) {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HB_DEFAULT;
+ } else if (!isLineEnd(chNext)) {
+ styler.ColourTo(i, StateToPrint);
+ state = SCE_HB_DEFAULT;
+ }
+ break;
+ case SCE_HP_DEFAULT:
+ case SCE_HP_START:
+ if (iswordstart(ch)) {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HP_WORD;
+ } else if ((ch == '<') && (chNext == '!') && (chNext2 == '-') &&
+ styler.SafeGetCharAt(i + 3) == '-') {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HP_COMMENTLINE;
+ } else if (ch == '#') {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HP_COMMENTLINE;
+ } else if (ch == '\"') {
+ styler.ColourTo(i - 1, StateToPrint);
+ if (chNext == '\"' && chNext2 == '\"') {
+ i += 2;
+ state = SCE_HP_TRIPLEDOUBLE;
+ ch = ' ';
+ chPrev = ' ';
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else {
+ // state = statePrintForState(SCE_HP_STRING,inScriptType);
+ state = SCE_HP_STRING;
+ }
+ } else if (ch == '\'') {
+ styler.ColourTo(i - 1, StateToPrint);
+ if (chNext == '\'' && chNext2 == '\'') {
+ i += 2;
+ state = SCE_HP_TRIPLE;
+ ch = ' ';
+ chPrev = ' ';
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else {
+ state = SCE_HP_CHARACTER;
+ }
+ } else if (isoperator(ch)) {
+ styler.ColourTo(i - 1, StateToPrint);
+ styler.ColourTo(i, statePrintForState(SCE_HP_OPERATOR, inScriptType));
+ } else if ((ch == ' ') || (ch == '\t')) {
+ if (state == SCE_HP_START) {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HP_DEFAULT;
+ }
+ }
+ break;
+ case SCE_HP_WORD:
+ if (!iswordchar(ch)) {
+ classifyWordHTPy(styler.GetStartSegment(), i - 1, keywords4, styler, prevWord, inScriptType);
+ state = SCE_HP_DEFAULT;
+ if (ch == '#') {
+ state = SCE_HP_COMMENTLINE;
+ } else if (ch == '\"') {
+ if (chNext == '\"' && chNext2 == '\"') {
+ i += 2;
+ state = SCE_HP_TRIPLEDOUBLE;
+ ch = ' ';
+ chPrev = ' ';
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else {
+ state = SCE_HP_STRING;
+ }
+ } else if (ch == '\'') {
+ if (chNext == '\'' && chNext2 == '\'') {
+ i += 2;
+ state = SCE_HP_TRIPLE;
+ ch = ' ';
+ chPrev = ' ';
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else {
+ state = SCE_HP_CHARACTER;
+ }
+ } else if (isoperator(ch)) {
+ styler.ColourTo(i, statePrintForState(SCE_HP_OPERATOR, inScriptType));
+ }
+ }
+ break;
+ case SCE_HP_COMMENTLINE:
+ if (ch == '\r' || ch == '\n') {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HP_DEFAULT;
+ }
+ break;
+ case SCE_HP_STRING:
+ if (ch == '\\') {
+ if (chNext == '\"' || chNext == '\'' || chNext == '\\') {
+ i++;
+ ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ } else if (ch == '\"') {
+ styler.ColourTo(i, StateToPrint);
+ state = SCE_HP_DEFAULT;
+ }
+ break;
+ case SCE_HP_CHARACTER:
+ if (ch == '\\') {
+ if (chNext == '\"' || chNext == '\'' || chNext == '\\') {
+ i++;
+ ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ } else if (ch == '\'') {
+ styler.ColourTo(i, StateToPrint);
+ state = SCE_HP_DEFAULT;
+ }
+ break;
+ case SCE_HP_TRIPLE:
+ if (ch == '\'' && chPrev == '\'' && chPrev2 == '\'') {
+ styler.ColourTo(i, StateToPrint);
+ state = SCE_HP_DEFAULT;
+ }
+ break;
+ case SCE_HP_TRIPLEDOUBLE:
+ if (ch == '\"' && chPrev == '\"' && chPrev2 == '\"') {
+ styler.ColourTo(i, StateToPrint);
+ state = SCE_HP_DEFAULT;
+ }
+ break;
+ ///////////// start - PHP state handling
+ case SCE_HPHP_WORD:
+ if (!iswordchar(ch)) {
+ classifyWordHTPHP(styler.GetStartSegment(), i - 1, keywords5, styler);
+ if (ch == '/' && chNext == '*') {
+ i++;
+ state = SCE_HPHP_COMMENT;
+ } else if (ch == '/' && chNext == '/') {
+ i++;
+ state = SCE_HPHP_COMMENTLINE;
+ } else if (ch == '#') {
+ state = SCE_HPHP_COMMENTLINE;
+ } else if (ch == '\"') {
+ state = SCE_HPHP_HSTRING;
+ strcpy(phpStringDelimiter, "\"");
+ } else if (styler.Match(i, "<<<")) {
+ state = SCE_HPHP_HSTRING;
+ i = FindPhpStringDelimiter(phpStringDelimiter, sizeof(phpStringDelimiter), i + 3, lengthDoc, styler);
+ } else if (ch == '\'') {
+ state = SCE_HPHP_SIMPLESTRING;
+ } else if (ch == '$' && IsPhpWordStart(chNext)) {
+ state = SCE_HPHP_VARIABLE;
+ } else if (isoperator(ch)) {
+ state = SCE_HPHP_OPERATOR;
+ } else {
+ state = SCE_HPHP_DEFAULT;
+ }
+ }
+ break;
+ case SCE_HPHP_NUMBER:
+ // recognize bases 8,10 or 16 integers OR floating-point numbers
+ if (!IsADigit(ch)
+ && strchr(".xXabcdefABCDEF", ch) == NULL
+ && ((ch != '-' && ch != '+') || (chPrev != 'e' && chPrev != 'E'))) {
+ styler.ColourTo(i - 1, SCE_HPHP_NUMBER);
+ if (isoperator(ch))
+ state = SCE_HPHP_OPERATOR;
+ else
+ state = SCE_HPHP_DEFAULT;
+ }
+ break;
+ case SCE_HPHP_VARIABLE:
+ if (!IsPhpWordChar(ch)) {
+ styler.ColourTo(i - 1, SCE_HPHP_VARIABLE);
+ if (isoperator(ch))
+ state = SCE_HPHP_OPERATOR;
+ else
+ state = SCE_HPHP_DEFAULT;
+ }
+ break;
+ case SCE_HPHP_COMMENT:
+ if (ch == '/' && chPrev == '*') {
+ styler.ColourTo(i, StateToPrint);
+ state = SCE_HPHP_DEFAULT;
+ }
+ break;
+ case SCE_HPHP_COMMENTLINE:
+ if (ch == '\r' || ch == '\n') {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HPHP_DEFAULT;
+ }
+ break;
+ case SCE_HPHP_HSTRING:
+ if (ch == '\\' && (phpStringDelimiter[0] == '\"' || chNext == '$' || chNext == '{')) {
+ // skip the next char
+ i++;
+ } else if (((ch == '{' && chNext == '$') || (ch == '$' && chNext == '{'))
+ && IsPhpWordStart(chNext2)) {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HPHP_COMPLEX_VARIABLE;
+ } else if (ch == '$' && IsPhpWordStart(chNext)) {
+ styler.ColourTo(i - 1, StateToPrint);
+ state = SCE_HPHP_HSTRING_VARIABLE;
+ } else if (styler.Match(i, phpStringDelimiter)) {
+ if (strlen(phpStringDelimiter) > 1)
+ i += strlen(phpStringDelimiter) - 1;
+ styler.ColourTo(i, StateToPrint);
+ state = SCE_HPHP_DEFAULT;
+ }
+ break;
+ case SCE_HPHP_SIMPLESTRING:
+ if (ch == '\\') {
+ // skip the next char
+ i++;
+ } else if (ch == '\'') {
+ styler.ColourTo(i, StateToPrint);
+ state = SCE_HPHP_DEFAULT;
+ }
+ break;
+ case SCE_HPHP_HSTRING_VARIABLE:
+ if (!IsPhpWordChar(ch)) {
+ styler.ColourTo(i - 1, StateToPrint);
+ i--; // strange but it works
+ state = SCE_HPHP_HSTRING;
+ }
+ break;
+ case SCE_HPHP_COMPLEX_VARIABLE:
+ if (ch == '}') {
+ styler.ColourTo(i, StateToPrint);
+ state = SCE_HPHP_HSTRING;
+ }
+ break;
+ case SCE_HPHP_OPERATOR:
+ case SCE_HPHP_DEFAULT:
+ styler.ColourTo(i - 1, StateToPrint);
+ if (IsADigit(ch) || (ch == '.' && IsADigit(chNext))) {
+ state = SCE_HPHP_NUMBER;
+ } else if (iswordstart(ch)) {
+ state = SCE_HPHP_WORD;
+ } else if (ch == '/' && chNext == '*') {
+ i++;
+ state = SCE_HPHP_COMMENT;
+ } else if (ch == '/' && chNext == '/') {
+ i++;
+ state = SCE_HPHP_COMMENTLINE;
+ } else if (ch == '#') {
+ state = SCE_HPHP_COMMENTLINE;
+ } else if (ch == '\"') {
+ state = SCE_HPHP_HSTRING;
+ strcpy(phpStringDelimiter, "\"");
+ } else if (styler.Match(i, "<<<")) {
+ state = SCE_HPHP_HSTRING;
+ i = FindPhpStringDelimiter(phpStringDelimiter, sizeof(phpStringDelimiter), i + 3, lengthDoc, styler);
+ } else if (ch == '\'') {
+ state = SCE_HPHP_SIMPLESTRING;
+ } else if (ch == '$' && IsPhpWordStart(chNext)) {
+ state = SCE_HPHP_VARIABLE;
+ } else if (isoperator(ch)) {
+ state = SCE_HPHP_OPERATOR;
+ } else if ((state == SCE_HPHP_OPERATOR) && (isspacechar(ch))) {
+ state = SCE_HPHP_DEFAULT;
+ }
+ break;
+ ///////////// end - PHP state handling
+ }
+
+ // Some of the above terminated their lexeme but since the same character starts
+ // the same class again, only reenter if non empty segment.
+
+ bool nonEmptySegment = i >= static_cast<int>(styler.GetStartSegment());
+ if (state == SCE_HB_DEFAULT) { // One of the above succeeded
+ if ((ch == '\"') && (nonEmptySegment)) {
+ state = SCE_HB_STRING;
+ } else if (ch == '\'') {
+ state = SCE_HB_COMMENTLINE;
+ } else if (iswordstart(ch)) {
+ state = SCE_HB_WORD;
+ } else if (isoperator(ch)) {
+ styler.ColourTo(i, SCE_HB_DEFAULT);
+ }
+ } else if (state == SCE_HBA_DEFAULT) { // One of the above succeeded
+ if ((ch == '\"') && (nonEmptySegment)) {
+ state = SCE_HBA_STRING;
+ } else if (ch == '\'') {
+ state = SCE_HBA_COMMENTLINE;
+ } else if (iswordstart(ch)) {
+ state = SCE_HBA_WORD;
+ } else if (isoperator(ch)) {
+ styler.ColourTo(i, SCE_HBA_DEFAULT);
+ }
+ } else if (state == SCE_HJ_DEFAULT) { // One of the above succeeded
+ if (ch == '/' && chNext == '*') {
+ if (styler.SafeGetCharAt(i + 2) == '*')
+ state = SCE_HJ_COMMENTDOC;
+ else
+ state = SCE_HJ_COMMENT;
+ } else if (ch == '/' && chNext == '/') {
+ state = SCE_HJ_COMMENTLINE;
+ } else if ((ch == '\"') && (nonEmptySegment)) {
+ state = SCE_HJ_DOUBLESTRING;
+ } else if ((ch == '\'') && (nonEmptySegment)) {
+ state = SCE_HJ_SINGLESTRING;
+ } else if (iswordstart(ch)) {
+ state = SCE_HJ_WORD;
+ } else if (isoperator(ch)) {
+ styler.ColourTo(i, statePrintForState(SCE_HJ_SYMBOLS, inScriptType));
+ }
+ }
+ }
+
+ StateToPrint = statePrintForState(state, inScriptType);
+ styler.ColourTo(lengthDoc - 1, StateToPrint);
+
+ // Fill in the real level of the next line, keeping the current flags as they will be filled in later
+ if (fold) {
+ int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
+ styler.SetLevel(lineCurrent, levelPrev | flagsNext);
+ }
+}
+
+static bool isASPScript(int state) {
+ return
+ (state >= SCE_HJA_START && state <= SCE_HJA_REGEX) ||
+ (state >= SCE_HBA_START && state <= SCE_HBA_STRINGEOL) ||
+ (state >= SCE_HPA_DEFAULT && state <= SCE_HPA_IDENTIFIER);
+}
+
+static void ColouriseHBAPiece(StyleContext &sc, WordList *keywordlists[]) {
+ WordList &keywordsVBS = *keywordlists[2];
+ if (sc.state == SCE_HBA_WORD) {
+ if (!IsAWordChar(sc.ch)) {
+ char s[100];
+ sc.GetCurrentLowered(s, sizeof(s));
+ if (keywordsVBS.InList(s)) {
+ if (strcmp(s, "rem") == 0) {
+ sc.ChangeState(SCE_HBA_COMMENTLINE);
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_HBA_DEFAULT);
+ }
+ } else {
+ sc.SetState(SCE_HBA_DEFAULT);
+ }
+ } else {
+ sc.ChangeState(SCE_HBA_IDENTIFIER);
+ sc.SetState(SCE_HBA_DEFAULT);
+ }
+ }
+ } else if (sc.state == SCE_HBA_NUMBER) {
+ if (!IsAWordChar(sc.ch)) {
+ sc.SetState(SCE_HBA_DEFAULT);
+ }
+ } else if (sc.state == SCE_HBA_STRING) {
+ if (sc.ch == '\"') {
+ sc.ForwardSetState(SCE_HBA_DEFAULT);
+ } else if (sc.ch == '\r' || sc.ch == '\n') {
+ sc.ChangeState(SCE_HBA_STRINGEOL);
+ sc.ForwardSetState(SCE_HBA_DEFAULT);
+ }
+ } else if (sc.state == SCE_HBA_COMMENTLINE) {
+ if (sc.ch == '\r' || sc.ch == '\n') {
+ sc.SetState(SCE_HBA_DEFAULT);
+ }
+ }
+
+ if (sc.state == SCE_HBA_DEFAULT) {
+ if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
+ sc.SetState(SCE_HBA_NUMBER);
+ } else if (IsAWordStart(sc.ch)) {
+ sc.SetState(SCE_HBA_WORD);
+ } else if (sc.ch == '\'') {
+ sc.SetState(SCE_HBA_COMMENTLINE);
+ } else if (sc.ch == '\"') {
+ sc.SetState(SCE_HBA_STRING);
+ }
+ }
+}
+
+static void ColouriseHTMLPiece(StyleContext &sc, WordList *keywordlists[]) {
+ WordList &keywordsTags = *keywordlists[0];
+ if (sc.state == SCE_H_COMMENT) {
+ if (sc.Match("-->")) {
+ sc.Forward();
+ sc.Forward();
+ sc.ForwardSetState(SCE_H_DEFAULT);
+ }
+ } else if (sc.state == SCE_H_ENTITY) {
+ if (sc.ch == ';') {
+ sc.ForwardSetState(SCE_H_DEFAULT);
+ } else if (sc.ch != '#' && (sc.ch < 0x80) && !isalnum(sc.ch) // Should check that '#' follows '&', but it is unlikely anyway...
+ && sc.ch != '.' && sc.ch != '-' && sc.ch != '_' && sc.ch != ':') { // valid in XML
+ sc.ChangeState(SCE_H_TAGUNKNOWN);
+ sc.SetState(SCE_H_DEFAULT);
+ }
+ } else if (sc.state == SCE_H_TAGUNKNOWN) {
+ if (!ishtmlwordchar(static_cast<char>(sc.ch)) && !((sc.ch == '/') && (sc.chPrev == '<')) && sc.ch != '[') {
+ char s[100];
+ sc.GetCurrentLowered(s, sizeof(s));
+ if (s[1] == '/') {
+ if (keywordsTags.InList(s + 2)) {
+ sc.ChangeState(SCE_H_TAG);
+ }
+ } else {
+ if (keywordsTags.InList(s + 1)) {
+ sc.ChangeState(SCE_H_TAG);
+ }
+ }
+ if (sc.ch == '>') {
+ sc.ForwardSetState(SCE_H_DEFAULT);
+ } else if (sc.Match('/', '>')) {
+ sc.SetState(SCE_H_TAGEND);
+ sc.Forward();
+ sc.ForwardSetState(SCE_H_DEFAULT);
+ } else {
+ sc.SetState(SCE_H_OTHER);
+ }
+ }
+ } else if (sc.state == SCE_H_ATTRIBUTE) {
+ if (!ishtmlwordchar(static_cast<char>(sc.ch))) {
+ char s[100];
+ sc.GetCurrentLowered(s, sizeof(s));
+ if (!keywordsTags.InList(s)) {
+ sc.ChangeState(SCE_H_ATTRIBUTEUNKNOWN);
+ }
+ sc.SetState(SCE_H_OTHER);
+ }
+ } else if (sc.state == SCE_H_OTHER) {
+ if (sc.ch == '>') {
+ sc.SetState(SCE_H_TAG);
+ sc.ForwardSetState(SCE_H_DEFAULT);
+ } else if (sc.Match('/', '>')) {
+ sc.SetState(SCE_H_TAG);
+ sc.Forward();
+ sc.ForwardSetState(SCE_H_DEFAULT);
+ } else if (sc.chPrev == '=') {
+ sc.SetState(SCE_H_VALUE);
+ }
+ } else if (sc.state == SCE_H_DOUBLESTRING) {
+ if (sc.ch == '\"') {
+ sc.ForwardSetState(SCE_H_OTHER);
+ }
+ } else if (sc.state == SCE_H_SINGLESTRING) {
+ if (sc.ch == '\'') {
+ sc.ForwardSetState(SCE_H_OTHER);
+ }
+ } else if (sc.state == SCE_H_NUMBER) {
+ if (!IsADigit(sc.ch)) {
+ sc.SetState(SCE_H_OTHER);
+ }
+ }
+
+ if (sc.state == SCE_H_DEFAULT) {
+ if (sc.ch == '<') {
+ if (sc.Match("<!--"))
+ sc.SetState(SCE_H_COMMENT);
+ else
+ sc.SetState(SCE_H_TAGUNKNOWN);
+ } else if (sc.ch == '&') {
+ sc.SetState(SCE_H_ENTITY);
+ }
+ } else if ((sc.state == SCE_H_OTHER) || (sc.state == SCE_H_VALUE)) {
+ if (sc.ch == '\"' && sc.chPrev == '=') {
+ sc.SetState(SCE_H_DOUBLESTRING);
+ } else if (sc.ch == '\'' && sc.chPrev == '=') {
+ sc.SetState(SCE_H_SINGLESTRING);
+ } else if (IsADigit(sc.ch)) {
+ sc.SetState(SCE_H_NUMBER);
+ } else if (sc.ch == '>') {
+ sc.SetState(SCE_H_TAG);
+ sc.ForwardSetState(SCE_H_DEFAULT);
+ } else if (ishtmlwordchar(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_H_ATTRIBUTE);
+ }
+ }
+}
+
+static void ColouriseASPPiece(StyleContext &sc, WordList *keywordlists[]) {
+ // Possibly exit current state to either SCE_H_DEFAULT or SCE_HBA_DEFAULT
+ if ((sc.state == SCE_H_ASPAT || isASPScript(sc.state)) && sc.Match('%', '>')) {
+ sc.SetState(SCE_H_ASP);
+ sc.Forward();
+ sc.ForwardSetState(SCE_H_DEFAULT);
+ }
+
+ // Handle some ASP script
+ if (sc.state >= SCE_HBA_START && sc.state <= SCE_HBA_STRINGEOL) {
+ ColouriseHBAPiece(sc, keywordlists);
+ } else if (sc.state >= SCE_H_DEFAULT && sc.state <= SCE_H_SGML_BLOCK_DEFAULT) {
+ ColouriseHTMLPiece(sc, keywordlists);
+ }
+
+ // Enter new sc.state
+ if ((sc.state == SCE_H_DEFAULT) || (sc.state == SCE_H_TAGUNKNOWN)) {
+ if (sc.Match('<', '%')) {
+ if (sc.state == SCE_H_TAGUNKNOWN)
+ sc.ChangeState(SCE_H_ASP);
+ else
+ sc.SetState(SCE_H_ASP);
+ sc.Forward();
+ sc.Forward();
+ if (sc.ch == '@') {
+ sc.ForwardSetState(SCE_H_ASPAT);
+ } else {
+ if (sc.ch == '=') {
+ sc.Forward();
+ }
+ sc.SetState(SCE_HBA_DEFAULT);
+ }
+ }
+ }
+}
+
+static void ColouriseASPDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+ Accessor &styler) {
+ // Lexer for HTML requires more lexical states (7 bits worth) than most lexers
+ StyleContext sc(startPos, length, initStyle, styler, 0x7f);
+ for (; sc.More(); sc.Forward()) {
+ ColouriseASPPiece(sc, keywordlists);
+ }
+ sc.Complete();
+}
+
+static void ColourisePHPPiece(StyleContext &sc, WordList *keywordlists[]) {
+ // Possibly exit current state to either SCE_H_DEFAULT or SCE_HBA_DEFAULT
+ if (sc.state >= SCE_HPHP_DEFAULT && sc.state <= SCE_HPHP_OPERATOR) {
+ if (!isPHPStringState(sc.state) &&
+ (sc.state != SCE_HPHP_COMMENT) &&
+ (sc.Match('?', '>'))) {
+ sc.SetState(SCE_H_QUESTION);
+ sc.Forward();
+ sc.ForwardSetState(SCE_H_DEFAULT);
+ }
+ }
+
+ if (sc.state >= SCE_H_DEFAULT && sc.state <= SCE_H_SGML_BLOCK_DEFAULT) {
+ ColouriseHTMLPiece(sc, keywordlists);
+ }
+
+ // Handle some PHP script
+ if (sc.state == SCE_HPHP_WORD) {
+ if (!IsPhpWordChar(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_HPHP_DEFAULT);
+ }
+ } else if (sc.state == SCE_HPHP_COMMENTLINE) {
+ if (sc.ch == '\r' || sc.ch == '\n') {
+ sc.SetState(SCE_HPHP_DEFAULT);
+ }
+ } else if (sc.state == SCE_HPHP_COMMENT) {
+ if (sc.Match('*', '/')) {
+ sc.Forward();
+ sc.Forward();
+ sc.SetState(SCE_HPHP_DEFAULT);
+ }
+ } else if (sc.state == SCE_HPHP_HSTRING) {
+ if (sc.ch == '\"') {
+ sc.ForwardSetState(SCE_HPHP_DEFAULT);
+ }
+ } else if (sc.state == SCE_HPHP_SIMPLESTRING) {
+ if (sc.ch == '\'') {
+ sc.ForwardSetState(SCE_HPHP_DEFAULT);
+ }
+ } else if (sc.state == SCE_HPHP_VARIABLE) {
+ if (!IsPhpWordChar(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_HPHP_DEFAULT);
+ }
+ } else if (sc.state == SCE_HPHP_OPERATOR) {
+ sc.SetState(SCE_HPHP_DEFAULT);
+ }
+
+ // Enter new sc.state
+ if ((sc.state == SCE_H_DEFAULT) || (sc.state == SCE_H_TAGUNKNOWN)) {
+ if (sc.Match("<?php")) {
+ sc.SetState(SCE_H_QUESTION);
+ sc.Forward();
+ sc.Forward();
+ sc.Forward();
+ sc.Forward();
+ sc.Forward();
+ sc.SetState(SCE_HPHP_DEFAULT);
+ }
+ }
+ if (sc.state == SCE_HPHP_DEFAULT) {
+ if (IsPhpWordStart(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_HPHP_WORD);
+ } else if (sc.ch == '#') {
+ sc.SetState(SCE_HPHP_COMMENTLINE);
+ } else if (sc.Match("<!--")) {
+ sc.SetState(SCE_HPHP_COMMENTLINE);
+ } else if (sc.Match('/', '/')) {
+ sc.SetState(SCE_HPHP_COMMENTLINE);
+ } else if (sc.Match('/', '*')) {
+ sc.SetState(SCE_HPHP_COMMENT);
+ } else if (sc.ch == '\"') {
+ sc.SetState(SCE_HPHP_HSTRING);
+ } else if (sc.ch == '\'') {
+ sc.SetState(SCE_HPHP_SIMPLESTRING);
+ } else if (sc.ch == '$' && IsPhpWordStart(static_cast<char>(sc.chNext))) {
+ sc.SetState(SCE_HPHP_VARIABLE);
+ } else if (isoperator(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_HPHP_OPERATOR);
+ }
+ }
+}
+
+static void ColourisePHPDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+ Accessor &styler) {
+ // Lexer for HTML requires more lexical states (7 bits worth) than most lexers
+ StyleContext sc(startPos, length, initStyle, styler, 0x7f);
+ for (; sc.More(); sc.Forward()) {
+ ColourisePHPPiece(sc, keywordlists);
+ }
+ sc.Complete();
+}
+
+static void ColourisePHPScriptDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+ Accessor &styler) {
+ if(startPos == 0) initStyle = SCE_HPHP_DEFAULT;
+ ColouriseHyperTextDoc(startPos,length,initStyle,keywordlists,styler);
+}
+
+static const char * const htmlWordListDesc[] = {
+ "HTML elements and attributes",
+ "JavaScript keywords",
+ "VBScript keywords",
+ "Python keywords",
+ "PHP keywords",
+ "SGML and DTD keywords",
+ 0,
+};
+
+static const char * const phpscriptWordListDesc[] = {
+ "", //Unused
+ "", //Unused
+ "", //Unused
+ "", //Unused
+ "PHP keywords",
+ "", //Unused
+ 0,
+};
+
+LexerModule lmHTML(SCLEX_HTML, ColouriseHyperTextDoc, "hypertext", 0, htmlWordListDesc, 7);
+LexerModule lmXML(SCLEX_XML, ColouriseHyperTextDoc, "xml", 0, htmlWordListDesc, 7);
+// SCLEX_ASP and SCLEX_PHP should not be used in new code: use SCLEX_HTML instead.
+LexerModule lmASP(SCLEX_ASP, ColouriseASPDoc, "asp", 0, htmlWordListDesc, 7);
+LexerModule lmPHP(SCLEX_PHP, ColourisePHPDoc, "php", 0, htmlWordListDesc, 7);
+LexerModule lmPHPSCRIPT(SCLEX_PHPSCRIPT, ColourisePHPScriptDoc, "phpscript", 0, phpscriptWordListDesc, 7);
diff --git a/src/LexHaskell.cpp b/src/LexHaskell.cpp
new file mode 100644
index 0000000..0e4be85
--- /dev/null
+++ b/src/LexHaskell.cpp
@@ -0,0 +1,263 @@
+/******************************************************************
+ * LexHaskell.cxx
+ *
+ * A haskell lexer for the scintilla code control.
+ * Some stuff "lended" from LexPython.cxx and LexCPP.cxx.
+ * External lexer stuff inspired from the caml external lexer.
+ *
+ * Written by Tobias Engvall - tumm at dtek dot chalmers dot se
+ *
+ *
+ * TODO:
+ * * Implement a folder :)
+ * * Nice Character-lexing (stuff inside '\''), LexPython has
+ * this.
+ *
+ *
+ *****************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+#ifdef BUILD_AS_EXTERNAL_LEXER
+
+#include "ExternalLexer.h"
+#include "WindowAccessor.h"
+
+#define BUILD_EXTERNAL_LEXER 0
+
+#endif
+
+// Max level of nested comments
+#define SCE_HA_COMMENTMAX SCE_HA_COMMENTBLOCK3
+
+
+enum kwType { kwOther, kwClass, kwData, kwInstance, kwImport, kwModule, kwType};
+
+static inline bool IsNewline(const int ch) {
+ return (ch == '\n' || ch == '\r');
+}
+
+static inline bool IsWhitespace(const int ch) {
+ return ( ch == ' '
+ || ch == '\t'
+ || IsNewline(ch) );
+}
+
+static inline bool IsAWordStart(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '_');
+}
+
+static inline bool IsAWordChar(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_' || ch == '\'');
+}
+
+static void ColorizeHaskellDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler) {
+
+ WordList &keywords = *keywordlists[0];
+
+ int kwLast = kwOther;
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ for (; sc.More(); sc.Forward()) {
+
+ // Check for state end
+ // Operator
+ if (sc.state == SCE_HA_OPERATOR) {
+ kwLast = kwOther;
+ sc.SetState(SCE_HA_DEFAULT);
+ }
+ // String
+ else if (sc.state == SCE_HA_STRING) {
+ if (sc.ch == '\"') {
+ sc.ForwardSetState(SCE_HA_DEFAULT);
+ }
+ }
+ // Char
+ else if (sc.state == SCE_HA_CHARACTER) {
+ if (sc.ch == '\'') {
+ sc.ForwardSetState(SCE_HA_DEFAULT);
+ }
+ }
+ // Number
+ else if (sc.state == SCE_HA_NUMBER) {
+ if (!IsADigit(sc.ch)) {
+ sc.SetState(SCE_HA_DEFAULT);
+ }
+ }
+ // Types, constructors, etc.
+ else if (sc.state == SCE_HA_CAPITAL) {
+ if (!IsAWordChar(sc.ch) || sc.ch == '.') {
+ sc.SetState(SCE_HA_DEFAULT);
+ }
+ }
+ // Identifier
+ else if (sc.state == SCE_HA_IDENTIFIER) {
+ if (!IsAWordChar(sc.ch)) {
+ char s[100];
+ sc.GetCurrent(s, sizeof(s));
+ int style = SCE_HA_IDENTIFIER;
+ if ((kwLast == kwImport) || (strcmp(s,"qualified") == 0) || (strcmp(s,"as") == 0)) {
+ style = SCE_HA_IMPORT;
+ } else if (keywords.InList(s)) {
+ style = SCE_HA_KEYWORD;
+ } else if (kwLast == kwData) {
+ style = SCE_HA_DATA;
+ } else if (kwLast == kwClass) {
+ style = SCE_HA_CLASS;
+ } else if (kwLast == kwModule) {
+ style = SCE_HA_MODULE;
+ } else if (isupper(s[0])) {
+ style = SCE_HA_CAPITAL;
+ }
+ sc.ChangeState(style);
+ sc.SetState(SCE_HA_DEFAULT);
+ if (style == SCE_HA_KEYWORD) {
+ if (0 == strcmp(s, "class"))
+ kwLast = kwClass;
+ else if (0 == strcmp(s, "data"))
+ kwLast = kwData;
+ else if (0 == strcmp(s, "instance"))
+ kwLast = kwInstance;
+ else if (0 == strcmp(s, "import"))
+ kwLast = kwImport;
+ else if (0 == strcmp(s, "module"))
+ kwLast = kwModule;
+ else
+ kwLast = kwOther;
+ } else if (style == SCE_HA_CLASS || style == SCE_HA_IMPORT ||
+ style == SCE_HA_MODULE || style == SCE_HA_CAPITAL ||
+ style == SCE_HA_DATA || style == SCE_HA_INSTANCE) {
+ kwLast = kwOther;
+ }
+ }
+ }
+ // Comments
+ // Oneliner
+ else if (sc.state == SCE_HA_COMMENTLINE) {
+ if (IsNewline(sc.ch))
+ sc.SetState(SCE_HA_DEFAULT);
+ }
+ // Nested
+ else if (sc.state >= SCE_HA_COMMENTBLOCK) {
+ if (sc.Match("{-")) {
+ if (sc.state < SCE_HA_COMMENTMAX)
+ sc.SetState(sc.state + 1);
+ }
+ else if (sc.Match("-}")) {
+ sc.Forward();
+ if (sc.state == SCE_HA_COMMENTBLOCK)
+ sc.ForwardSetState(SCE_HA_DEFAULT);
+ else
+ sc.ForwardSetState(sc.state - 1);
+ }
+ }
+ // New state?
+ if (sc.state == SCE_HA_DEFAULT) {
+ // Digit
+ if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
+ sc.SetState(SCE_HA_NUMBER);
+ }
+ // Comment line
+ else if (sc.Match("--")) {
+ sc.SetState(SCE_HA_COMMENTLINE);
+ // Comment block
+ }
+ else if (sc.Match("{-")) {
+ sc.SetState(SCE_HA_COMMENTBLOCK);
+ }
+ // String
+ else if (sc.Match('\"')) {
+ sc.SetState(SCE_HA_STRING);
+ }
+ // Character
+ else if (sc.Match('\'') && IsWhitespace(sc.GetRelative(-1)) ) {
+ sc.SetState(SCE_HA_CHARACTER);
+ }
+ // Stringstart
+ else if (sc.Match('\"')) {
+ sc.SetState(SCE_HA_STRING);
+ }
+ // Operator
+ else if (isascii(sc.ch) && isoperator(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_HA_OPERATOR);
+ }
+ // Keyword
+ else if (IsAWordStart(sc.ch)) {
+ sc.SetState(SCE_HA_IDENTIFIER);
+ }
+
+ }
+ }
+ sc.Complete();
+}
+
+// External stuff - used for dynamic-loading, not implemented in wxStyledTextCtrl yet.
+// Inspired by the caml external lexer - Credits to Robert Roessler - http://www.rftp.com
+#ifdef BUILD_EXTERNAL_LEXER
+static const char* LexerName = "haskell";
+
+void EXT_LEXER_DECL Lex(unsigned int lexer, unsigned int startPos, int length, int initStyle,
+ char *words[], WindowID window, char *props)
+{
+ PropSet ps;
+ ps.SetMultiple(props);
+ WindowAccessor wa(window, ps);
+
+ int nWL = 0;
+ for (; words[nWL]; nWL++) ;
+ WordList** wl = new WordList* [nWL + 1];
+ int i = 0;
+ for (; i<nWL; i++)
+ {
+ wl[i] = new WordList();
+ wl[i]->Set(words[i]);
+ }
+ wl[i] = 0;
+
+ ColorizeHaskellDoc(startPos, length, initStyle, wl, wa);
+ wa.Flush();
+ for (i=nWL-1;i>=0;i--)
+ delete wl[i];
+ delete [] wl;
+}
+
+void EXT_LEXER_DECL Fold (unsigned int lexer, unsigned int startPos, int length, int initStyle,
+ char *words[], WindowID window, char *props)
+{
+
+}
+
+int EXT_LEXER_DECL GetLexerCount()
+{
+ return 1;
+}
+
+void EXT_LEXER_DECL GetLexerName(unsigned int Index, char *name, int buflength)
+{
+ if (buflength > 0) {
+ buflength--;
+ int n = strlen(LexerName);
+ if (n > buflength)
+ n = buflength;
+ memcpy(name, LexerName, n), name[n] = '\0';
+ }
+}
+#endif
+
+LexerModule lmHaskell(SCLEX_HASKELL, ColorizeHaskellDoc, "haskell");
+
diff --git a/src/LexInno.cpp b/src/LexInno.cpp
new file mode 100644
index 0000000..ff99da7
--- /dev/null
+++ b/src/LexInno.cpp
@@ -0,0 +1,290 @@
+// Scintilla source code edit control
+/** @file LexInno.cxx
+ ** Lexer for Inno Setup scripts.
+ **/
+// Written by Friedrich Vedder <fvedd@t-online.de>, using code from LexOthers.cxx.
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+static void ColouriseInnoDoc(unsigned int startPos, int length, int, WordList *keywordLists[], Accessor &styler) {
+ int state = SCE_INNO_DEFAULT;
+ char chPrev;
+ char ch = 0;
+ char chNext = styler[startPos];
+ int lengthDoc = startPos + length;
+ char *buffer = new char[length];
+ int bufferCount = 0;
+ bool isBOL, isEOL, isWS, isBOLWS = 0;
+
+ WordList &sectionKeywords = *keywordLists[0];
+ WordList &standardKeywords = *keywordLists[1];
+ WordList &parameterKeywords = *keywordLists[2];
+ WordList &preprocessorKeywords = *keywordLists[3];
+ WordList &pascalKeywords = *keywordLists[4];
+ WordList &userKeywords = *keywordLists[5];
+
+ // Go through all provided text segment
+ // using the hand-written state machine shown below
+ styler.StartAt(startPos);
+ styler.StartSegment(startPos);
+ for (int i = startPos; i < lengthDoc; i++) {
+ chPrev = ch;
+ ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+
+ if (styler.IsLeadByte(ch)) {
+ chNext = styler.SafeGetCharAt(i + 2);
+ i++;
+ continue;
+ }
+
+ isBOL = (chPrev == 0) || (chPrev == '\n') || (chPrev == '\r' && ch != '\n');
+ isBOLWS = (isBOL) ? 1 : (isBOLWS && (chPrev == ' ' || chPrev == '\t'));
+ isEOL = (ch == '\n' || ch == '\r');
+ isWS = (ch == ' ' || ch == '\t');
+
+ switch(state) {
+ case SCE_INNO_DEFAULT:
+ if (ch == ';' && isBOLWS) {
+ // Start of a comment
+ state = SCE_INNO_COMMENT;
+ } else if (ch == '[' && isBOLWS) {
+ // Start of a section name
+ bufferCount = 0;
+ state = SCE_INNO_SECTION;
+ } else if (ch == '#' && isBOLWS) {
+ // Start of a preprocessor directive
+ state = SCE_INNO_PREPROC;
+ } else if (ch == '{' && chNext == '#') {
+ // Start of a preprocessor inline directive
+ state = SCE_INNO_PREPROC_INLINE;
+ } else if ((ch == '{' && (chNext == ' ' || chNext == '\t'))
+ || (ch == '(' && chNext == '*')) {
+ // Start of a Pascal comment
+ state = SCE_INNO_COMMENT_PASCAL;
+ } else if (ch == '"') {
+ // Start of a double-quote string
+ state = SCE_INNO_STRING_DOUBLE;
+ } else if (ch == '\'') {
+ // Start of a single-quote string
+ state = SCE_INNO_STRING_SINGLE;
+ } else if (isascii(ch) && (isalpha(ch) || (ch == '_'))) {
+ // Start of an identifier
+ bufferCount = 0;
+ buffer[bufferCount++] = static_cast<char>(tolower(ch));
+ state = SCE_INNO_IDENTIFIER;
+ } else {
+ // Style it the default style
+ styler.ColourTo(i,SCE_INNO_DEFAULT);
+ }
+ break;
+
+ case SCE_INNO_COMMENT:
+ if (isEOL) {
+ state = SCE_INNO_DEFAULT;
+ styler.ColourTo(i,SCE_INNO_COMMENT);
+ }
+ break;
+
+ case SCE_INNO_IDENTIFIER:
+ if (isascii(ch) && (isalnum(ch) || (ch == '_'))) {
+ buffer[bufferCount++] = static_cast<char>(tolower(ch));
+ } else {
+ state = SCE_INNO_DEFAULT;
+ buffer[bufferCount] = '\0';
+
+ // Check if the buffer contains a keyword
+ if (standardKeywords.InList(buffer)) {
+ styler.ColourTo(i-1,SCE_INNO_KEYWORD);
+ } else if (parameterKeywords.InList(buffer)) {
+ styler.ColourTo(i-1,SCE_INNO_PARAMETER);
+ } else if (pascalKeywords.InList(buffer)) {
+ styler.ColourTo(i-1,SCE_INNO_KEYWORD_PASCAL);
+ } else if (userKeywords.InList(buffer)) {
+ styler.ColourTo(i-1,SCE_INNO_KEYWORD_USER);
+ } else {
+ styler.ColourTo(i-1,SCE_INNO_DEFAULT);
+ }
+
+ // Push back the faulty character
+ chNext = styler[i--];
+ ch = chPrev;
+ }
+ break;
+
+ case SCE_INNO_SECTION:
+ if (ch == ']') {
+ state = SCE_INNO_DEFAULT;
+ buffer[bufferCount] = '\0';
+
+ // Check if the buffer contains a section name
+ if (sectionKeywords.InList(buffer)) {
+ styler.ColourTo(i,SCE_INNO_SECTION);
+ } else {
+ styler.ColourTo(i,SCE_INNO_DEFAULT);
+ }
+ } else if (isascii(ch) && (isalnum(ch) || (ch == '_'))) {
+ buffer[bufferCount++] = static_cast<char>(tolower(ch));
+ } else {
+ state = SCE_INNO_DEFAULT;
+ styler.ColourTo(i,SCE_INNO_DEFAULT);
+ }
+ break;
+
+ case SCE_INNO_PREPROC:
+ if (isWS || isEOL) {
+ if (isascii(chPrev) && isalpha(chPrev)) {
+ state = SCE_INNO_DEFAULT;
+ buffer[bufferCount] = '\0';
+
+ // Check if the buffer contains a preprocessor directive
+ if (preprocessorKeywords.InList(buffer)) {
+ styler.ColourTo(i-1,SCE_INNO_PREPROC);
+ } else {
+ styler.ColourTo(i-1,SCE_INNO_DEFAULT);
+ }
+
+ // Push back the faulty character
+ chNext = styler[i--];
+ ch = chPrev;
+ }
+ } else if (isascii(ch) && isalpha(ch)) {
+ if (chPrev == '#' || chPrev == ' ' || chPrev == '\t')
+ bufferCount = 0;
+ buffer[bufferCount++] = static_cast<char>(tolower(ch));
+ }
+ break;
+
+ case SCE_INNO_STRING_DOUBLE:
+ if (ch == '"' || isEOL) {
+ state = SCE_INNO_DEFAULT;
+ styler.ColourTo(i,SCE_INNO_DEFAULT);
+ }
+ break;
+
+ case SCE_INNO_STRING_SINGLE:
+ if (ch == '\'' || isEOL) {
+ state = SCE_INNO_DEFAULT;
+ styler.ColourTo(i,SCE_INNO_DEFAULT);
+ }
+ break;
+
+ case SCE_INNO_PREPROC_INLINE:
+ if (ch == '}') {
+ state = SCE_INNO_DEFAULT;
+ styler.ColourTo(i,SCE_INNO_PREPROC_INLINE);
+ } else if (isEOL) {
+ state = SCE_INNO_DEFAULT;
+ styler.ColourTo(i,SCE_INNO_DEFAULT);
+ }
+ break;
+
+ case SCE_INNO_COMMENT_PASCAL:
+ if (ch == '}' || (ch == ')' && chPrev == '*')) {
+ state = SCE_INNO_DEFAULT;
+ styler.ColourTo(i,SCE_INNO_COMMENT_PASCAL);
+ } else if (isEOL) {
+ state = SCE_INNO_DEFAULT;
+ styler.ColourTo(i,SCE_INNO_DEFAULT);
+ }
+ break;
+
+ }
+ }
+ delete []buffer;
+}
+
+static const char * const innoWordListDesc[] = {
+ "Sections",
+ "Keywords",
+ "Parameters",
+ "Preprocessor directives",
+ "Pascal keywords",
+ "User defined keywords",
+ 0
+};
+
+static void FoldInnoDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+
+ unsigned int endPos = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+
+ char chNext = styler[startPos];
+ int styleNext = styler.StyleAt(startPos);
+ bool headerPoint = false;
+ int lev;
+
+ for (unsigned int i = startPos; i < endPos; i++) {
+ char ch = chNext;
+ chNext = styler[i+1];
+
+ int style = styleNext;
+ styleNext = styler.StyleAt(i + 1);
+ bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
+
+ if (style == SCE_INNO_SECTION)
+ headerPoint = true;
+
+ if (atEOL) {
+ lev = SC_FOLDLEVELBASE;
+
+ if (lineCurrent > 0) {
+ int levelPrevious = styler.LevelAt(lineCurrent - 1);
+
+ if (levelPrevious & SC_FOLDLEVELHEADERFLAG)
+ lev = SC_FOLDLEVELBASE + 1;
+ else
+ lev = levelPrevious & SC_FOLDLEVELNUMBERMASK;
+ }
+
+ if (headerPoint)
+ lev = SC_FOLDLEVELBASE;
+
+ if (visibleChars == 0 && foldCompact)
+ lev |= SC_FOLDLEVELWHITEFLAG;
+
+ if (headerPoint)
+ lev |= SC_FOLDLEVELHEADERFLAG;
+
+ if (lev != styler.LevelAt(lineCurrent))
+ styler.SetLevel(lineCurrent, lev);
+
+ lineCurrent++;
+ visibleChars = 0;
+ headerPoint = false;
+ }
+ if (!isspacechar(ch))
+ visibleChars++;
+ }
+
+ if (lineCurrent > 0) {
+ int levelPrevious = styler.LevelAt(lineCurrent - 1);
+
+ if (levelPrevious & SC_FOLDLEVELHEADERFLAG)
+ lev = SC_FOLDLEVELBASE + 1;
+ else
+ lev = levelPrevious & SC_FOLDLEVELNUMBERMASK;
+ } else {
+ lev = SC_FOLDLEVELBASE;
+ }
+ int flagsNext = styler.LevelAt(lineCurrent);
+ styler.SetLevel(lineCurrent, lev | flagsNext & ~SC_FOLDLEVELNUMBERMASK);
+}
+
+LexerModule lmInno(SCLEX_INNOSETUP, ColouriseInnoDoc, "inno", FoldInnoDoc, innoWordListDesc);
diff --git a/src/LexKix.cpp b/src/LexKix.cpp
new file mode 100755
index 0000000..e439d4d
--- /dev/null
+++ b/src/LexKix.cpp
@@ -0,0 +1,122 @@
+// Scintilla source code edit control
+/** @file LexKix.cxx
+ ** Lexer for KIX-Scripts.
+ **/
+// Copyright 2004 by Manfred Becker <manfred@becker-trdf.de>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+// Extended to accept accented characters
+static inline bool IsAWordChar(int ch) {
+ return ch >= 0x80 || isalnum(ch) || ch == '_';
+}
+
+static inline bool IsOperator(const int ch) {
+ return (ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '&' || ch == '|' || ch == '<' || ch == '>' || ch == '=');
+}
+
+static void ColouriseKixDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler) {
+
+ WordList &keywords = *keywordlists[0];
+ WordList &keywords2 = *keywordlists[1];
+ WordList &keywords3 = *keywordlists[2];
+// WordList &keywords4 = *keywordlists[3];
+
+ styler.StartAt(startPos);
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ for (; sc.More(); sc.Forward()) {
+
+ if (sc.state == SCE_KIX_COMMENT) {
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_KIX_DEFAULT);
+ }
+ } else if (sc.state == SCE_KIX_STRING1) {
+ // This is a doubles quotes string
+ if (sc.ch == '\"') {
+ sc.ForwardSetState(SCE_KIX_DEFAULT);
+ }
+ } else if (sc.state == SCE_KIX_STRING2) {
+ // This is a single quote string
+ if (sc.ch == '\'') {
+ sc.ForwardSetState(SCE_KIX_DEFAULT);
+ }
+ } else if (sc.state == SCE_KIX_NUMBER) {
+ if (!IsADigit(sc.ch)) {
+ sc.SetState(SCE_KIX_DEFAULT);
+ }
+ } else if (sc.state == SCE_KIX_VAR) {
+ if (!IsAWordChar(sc.ch)) {
+ sc.SetState(SCE_KIX_DEFAULT);
+ }
+ } else if (sc.state == SCE_KIX_MACRO) {
+ if (!IsAWordChar(sc.ch) && !IsADigit(sc.ch)) {
+ char s[100];
+ sc.GetCurrentLowered(s, sizeof(s));
+
+ if (!keywords3.InList(&s[1])) {
+ sc.ChangeState(SCE_KIX_DEFAULT);
+ }
+ sc.SetState(SCE_KIX_DEFAULT);
+ }
+ } else if (sc.state == SCE_KIX_OPERATOR) {
+ if (!IsOperator(sc.ch)) {
+ sc.SetState(SCE_KIX_DEFAULT);
+ }
+ } else if (sc.state == SCE_KIX_IDENTIFIER) {
+ if (!IsAWordChar(sc.ch)) {
+ char s[100];
+ sc.GetCurrentLowered(s, sizeof(s));
+
+ if (keywords.InList(s)) {
+ sc.ChangeState(SCE_KIX_KEYWORD);
+ } else if (keywords2.InList(s)) {
+ sc.ChangeState(SCE_KIX_FUNCTIONS);
+ }
+ sc.SetState(SCE_KIX_DEFAULT);
+ }
+ }
+
+ // Determine if a new state should be entered.
+ if (sc.state == SCE_KIX_DEFAULT) {
+ if (sc.ch == ';') {
+ sc.SetState(SCE_KIX_COMMENT);
+ } else if (sc.ch == '\"') {
+ sc.SetState(SCE_KIX_STRING1);
+ } else if (sc.ch == '\'') {
+ sc.SetState(SCE_KIX_STRING2);
+ } else if (sc.ch == '$') {
+ sc.SetState(SCE_KIX_VAR);
+ } else if (sc.ch == '@') {
+ sc.SetState(SCE_KIX_MACRO);
+ } else if (IsADigit(sc.ch) || ((sc.ch == '.' || sc.ch == '&') && IsADigit(sc.chNext))) {
+ sc.SetState(SCE_KIX_NUMBER);
+ } else if (IsOperator(sc.ch)) {
+ sc.SetState(SCE_KIX_OPERATOR);
+ } else if (IsAWordChar(sc.ch)) {
+ sc.SetState(SCE_KIX_IDENTIFIER);
+ }
+ }
+ }
+ sc.Complete();
+}
+
+
+LexerModule lmKix(SCLEX_KIX, ColouriseKixDoc, "kix");
+
diff --git a/src/LexLisp.cpp b/src/LexLisp.cpp
new file mode 100755
index 0000000..91385f0
--- /dev/null
+++ b/src/LexLisp.cpp
@@ -0,0 +1,275 @@
+// Scintilla source code edit control
+/** @file LexLisp.cxx
+ ** Lexer for Lisp.
+ ** Written by Alexey Yutkin.
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+#include "StyleContext.h"
+
+#define SCE_LISP_CHARACTER 29
+#define SCE_LISP_MACRO 30
+#define SCE_LISP_MACRO_DISPATCH 31
+
+static inline bool isLispoperator(char ch) {
+ if (isascii(ch) && isalnum(ch))
+ return false;
+ if (ch == '\'' || ch == '`' || ch == '(' || ch == ')' )
+ return true;
+ return false;
+}
+
+static inline bool isLispwordstart(char ch) {
+ return isascii(ch) && ch != ';' && !isspacechar(ch) && !isLispoperator(ch) &&
+ ch != '\n' && ch != '\r' && ch != '\"';
+}
+
+
+static void classifyWordLisp(unsigned int start, unsigned int end, WordList &keywords, WordList &keywords_kw, Accessor &styler) {
+ PLATFORM_ASSERT(end >= start);
+ char s[100];
+ unsigned int i;
+ bool digit_flag = true;
+ for (i = 0; (i < end - start + 1) && (i < 99); i++) {
+ s[i] = styler[start + i];
+ s[i + 1] = '\0';
+ if (!isdigit(s[i]) && (s[i] != '.')) digit_flag = false;
+ }
+ char chAttr = SCE_LISP_IDENTIFIER;
+
+ if(digit_flag) chAttr = SCE_LISP_NUMBER;
+ else {
+ if (keywords.InList(s)) {
+ chAttr = SCE_LISP_KEYWORD;
+ } else if (keywords_kw.InList(s)) {
+ chAttr = SCE_LISP_KEYWORD_KW;
+ } else if ((s[0] == '*' && s[i-1] == '*') ||
+ (s[0] == '+' && s[i-1] == '+')) {
+ chAttr = SCE_LISP_SPECIAL;
+ }
+ }
+ styler.ColourTo(end, chAttr);
+ return;
+}
+
+
+static void ColouriseLispDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+ Accessor &styler) {
+
+ WordList &keywords = *keywordlists[0];
+ WordList &keywords_kw = *keywordlists[1];
+
+ styler.StartAt(startPos);
+
+ int state = initStyle, radix = -1;
+ char chNext = styler[startPos];
+ unsigned int lengthDoc = startPos + length;
+ styler.StartSegment(startPos);
+ for (unsigned int i = startPos; i < lengthDoc; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+
+ bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
+
+ if (styler.IsLeadByte(ch)) {
+ chNext = styler.SafeGetCharAt(i + 2);
+ i += 1;
+ continue;
+ }
+
+ if (state == SCE_LISP_DEFAULT) {
+ if (ch == '#') {
+ styler.ColourTo(i - 1, state);
+ radix = -1;
+ state = SCE_LISP_MACRO_DISPATCH;
+ } else if (isLispwordstart(ch)) {
+ styler.ColourTo(i - 1, state);
+ state = SCE_LISP_IDENTIFIER;
+ }
+ else if (ch == ';') {
+ styler.ColourTo(i - 1, state);
+ state = SCE_LISP_COMMENT;
+ }
+ else if (isLispoperator(ch) || ch=='\'') {
+ styler.ColourTo(i - 1, state);
+ styler.ColourTo(i, SCE_LISP_OPERATOR);
+ if (ch=='\'' && isLispwordstart(chNext)) {
+ state = SCE_LISP_SYMBOL;
+ }
+ }
+ else if (ch == '\"') {
+ styler.ColourTo(i - 1, state);
+ state = SCE_LISP_STRING;
+ }
+ } else if (state == SCE_LISP_IDENTIFIER || state == SCE_LISP_SYMBOL) {
+ if (!isLispwordstart(ch)) {
+ if (state == SCE_LISP_IDENTIFIER) {
+ classifyWordLisp(styler.GetStartSegment(), i - 1, keywords, keywords_kw, styler);
+ } else {
+ styler.ColourTo(i - 1, state);
+ }
+ state = SCE_LISP_DEFAULT;
+ } /*else*/
+ if (isLispoperator(ch) || ch=='\'') {
+ styler.ColourTo(i - 1, state);
+ styler.ColourTo(i, SCE_LISP_OPERATOR);
+ if (ch=='\'' && isLispwordstart(chNext)) {
+ state = SCE_LISP_SYMBOL;
+ }
+ }
+ } else if (state == SCE_LISP_MACRO_DISPATCH) {
+ if (!isdigit(ch)) {
+ if (ch != 'r' && ch != 'R' && (i - styler.GetStartSegment()) > 1) {
+ state = SCE_LISP_DEFAULT;
+ } else {
+ switch (ch) {
+ case '|': state = SCE_LISP_MULTI_COMMENT; break;
+ case 'o':
+ case 'O': radix = 8; state = SCE_LISP_MACRO; break;
+ case 'x':
+ case 'X': radix = 16; state = SCE_LISP_MACRO; break;
+ case 'b':
+ case 'B': radix = 2; state = SCE_LISP_MACRO; break;
+ case '\\': state = SCE_LISP_CHARACTER; break;
+ case ':':
+ case '-':
+ case '+': state = SCE_LISP_MACRO; break;
+ case '\'': if (isLispwordstart(chNext)) {
+ state = SCE_LISP_SPECIAL;
+ } else {
+ styler.ColourTo(i - 1, SCE_LISP_DEFAULT);
+ styler.ColourTo(i, SCE_LISP_OPERATOR);
+ state = SCE_LISP_DEFAULT;
+ }
+ break;
+ default: if (isLispoperator(ch)) {
+ styler.ColourTo(i - 1, SCE_LISP_DEFAULT);
+ styler.ColourTo(i, SCE_LISP_OPERATOR);
+ }
+ state = SCE_LISP_DEFAULT;
+ break;
+ }
+ }
+ }
+ } else if (state == SCE_LISP_MACRO) {
+ if (isLispwordstart(ch) && (radix == -1 || IsADigit(ch, radix))) {
+ state = SCE_LISP_SPECIAL;
+ } else {
+ state = SCE_LISP_DEFAULT;
+ }
+ } else if (state == SCE_LISP_CHARACTER) {
+ if (isLispoperator(ch)) {
+ styler.ColourTo(i, SCE_LISP_SPECIAL);
+ state = SCE_LISP_DEFAULT;
+ } else if (isLispwordstart(ch)) {
+ styler.ColourTo(i, SCE_LISP_SPECIAL);
+ state = SCE_LISP_SPECIAL;
+ } else {
+ state = SCE_LISP_DEFAULT;
+ }
+ } else if (state == SCE_LISP_SPECIAL) {
+ if (!isLispwordstart(ch) || (radix != -1 && !IsADigit(ch, radix))) {
+ styler.ColourTo(i - 1, state);
+ state = SCE_LISP_DEFAULT;
+ }
+ if (isLispoperator(ch) || ch=='\'') {
+ styler.ColourTo(i - 1, state);
+ styler.ColourTo(i, SCE_LISP_OPERATOR);
+ if (ch=='\'' && isLispwordstart(chNext)) {
+ state = SCE_LISP_SYMBOL;
+ }
+ }
+ } else {
+ if (state == SCE_LISP_COMMENT) {
+ if (atEOL) {
+ styler.ColourTo(i - 1, state);
+ state = SCE_LISP_DEFAULT;
+ }
+ } else if (state == SCE_LISP_MULTI_COMMENT) {
+ if (ch == '|' && chNext == '#') {
+ i++;
+ chNext = styler.SafeGetCharAt(i + 1);
+ styler.ColourTo(i, state);
+ state = SCE_LISP_DEFAULT;
+ }
+ } else if (state == SCE_LISP_STRING) {
+ if (ch == '\\') {
+ if (chNext == '\"' || chNext == '\'' || chNext == '\\') {
+ i++;
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ } else if (ch == '\"') {
+ styler.ColourTo(i, state);
+ state = SCE_LISP_DEFAULT;
+ }
+ }
+ }
+
+ }
+ styler.ColourTo(lengthDoc - 1, state);
+}
+
+static void FoldLispDoc(unsigned int startPos, int length, int /* initStyle */, WordList *[],
+ Accessor &styler) {
+ unsigned int lengthDoc = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
+ int levelCurrent = levelPrev;
+ char chNext = styler[startPos];
+ int styleNext = styler.StyleAt(startPos);
+ for (unsigned int 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_LISP_OPERATOR) {
+ 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 lispWordListDesc[] = {
+ "Functions and special operators",
+ "Keywords",
+ 0
+};
+
+LexerModule lmLISP(SCLEX_LISP, ColouriseLispDoc, "lisp", FoldLispDoc, lispWordListDesc);
diff --git a/src/LexLout.cpp b/src/LexLout.cpp
new file mode 100755
index 0000000..9d1a45a
--- /dev/null
+++ b/src/LexLout.cpp
@@ -0,0 +1,208 @@
+// Scintilla source code edit control
+/** @file LexLout.cxx
+ ** Lexer for the Basser Lout (>= version 3) typesetting language
+ **/
+// Copyright 2003 by Kein-Hong Man <mkh@pl.jaring.my>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+static inline bool IsAWordChar(const int ch) {
+ return (ch < 0x80) && (isalpha(ch) || ch == '@' || ch == '_');
+}
+
+static inline bool IsAnOther(const int ch) {
+ return (ch < 0x80) && (ch == '{' || ch == '}' ||
+ ch == '!' || ch == '$' || ch == '%' || ch == '&' || ch == '\'' ||
+ ch == '(' || ch == ')' || ch == '*' || ch == '+' || ch == ',' ||
+ ch == '-' || ch == '.' || ch == '/' || ch == ':' || ch == ';' ||
+ ch == '<' || ch == '=' || ch == '>' || ch == '?' || ch == '[' ||
+ ch == ']' || ch == '^' || ch == '`' || ch == '|' || ch == '~');
+}
+
+static void ColouriseLoutDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler) {
+
+ WordList &keywords = *keywordlists[0];
+ WordList &keywords2 = *keywordlists[1];
+ WordList &keywords3 = *keywordlists[2];
+
+ int visibleChars = 0;
+ int firstWordInLine = 0;
+ int leadingAtSign = 0;
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ for (; sc.More(); sc.Forward()) {
+
+ if (sc.atLineStart && (sc.state == SCE_LOUT_STRING)) {
+ // Prevent SCE_LOUT_STRINGEOL from leaking back to previous line
+ sc.SetState(SCE_LOUT_STRING);
+ }
+
+ // Determine if the current state should terminate.
+ if (sc.state == SCE_LOUT_COMMENT) {
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_LOUT_DEFAULT);
+ visibleChars = 0;
+ }
+ } else if (sc.state == SCE_LOUT_NUMBER) {
+ if (!IsADigit(sc.ch) && sc.ch != '.') {
+ sc.SetState(SCE_LOUT_DEFAULT);
+ }
+ } else if (sc.state == SCE_LOUT_STRING) {
+ if (sc.ch == '\\') {
+ if (sc.chNext == '\"' || sc.chNext == '\\') {
+ sc.Forward();
+ }
+ } else if (sc.ch == '\"') {
+ sc.ForwardSetState(SCE_LOUT_DEFAULT);
+ } else if (sc.atLineEnd) {
+ sc.ChangeState(SCE_LOUT_STRINGEOL);
+ sc.ForwardSetState(SCE_LOUT_DEFAULT);
+ visibleChars = 0;
+ }
+ } else if (sc.state == SCE_LOUT_IDENTIFIER) {
+ if (!IsAWordChar(sc.ch)) {
+ char s[100];
+ sc.GetCurrent(s, sizeof(s));
+
+ if (leadingAtSign) {
+ if (keywords.InList(s)) {
+ sc.ChangeState(SCE_LOUT_WORD);
+ } else {
+ sc.ChangeState(SCE_LOUT_WORD4);
+ }
+ } else if (firstWordInLine && keywords3.InList(s)) {
+ sc.ChangeState(SCE_LOUT_WORD3);
+ }
+ sc.SetState(SCE_LOUT_DEFAULT);
+ }
+ } else if (sc.state == SCE_LOUT_OPERATOR) {
+ if (!IsAnOther(sc.ch)) {
+ char s[100];
+ sc.GetCurrent(s, sizeof(s));
+
+ if (keywords2.InList(s)) {
+ sc.ChangeState(SCE_LOUT_WORD2);
+ }
+ sc.SetState(SCE_LOUT_DEFAULT);
+ }
+ }
+
+ // Determine if a new state should be entered.
+ if (sc.state == SCE_LOUT_DEFAULT) {
+ if (sc.ch == '#') {
+ sc.SetState(SCE_LOUT_COMMENT);
+ } else if (sc.ch == '\"') {
+ sc.SetState(SCE_LOUT_STRING);
+ } else if (IsADigit(sc.ch) ||
+ (sc.ch == '.' && IsADigit(sc.chNext))) {
+ sc.SetState(SCE_LOUT_NUMBER);
+ } else if (IsAWordChar(sc.ch)) {
+ firstWordInLine = (visibleChars == 0);
+ leadingAtSign = (sc.ch == '@');
+ sc.SetState(SCE_LOUT_IDENTIFIER);
+ } else if (IsAnOther(sc.ch)) {
+ sc.SetState(SCE_LOUT_OPERATOR);
+ }
+ }
+
+ if (sc.atLineEnd) {
+ // Reset states to begining of colourise so no surprises
+ // if different sets of lines lexed.
+ visibleChars = 0;
+ }
+ if (!IsASpace(sc.ch)) {
+ visibleChars++;
+ }
+ }
+ sc.Complete();
+}
+
+static void FoldLoutDoc(unsigned int startPos, int length, int, WordList *[],
+ Accessor &styler) {
+
+ unsigned int endPos = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
+ int levelCurrent = levelPrev;
+ char chNext = styler[startPos];
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ int styleNext = styler.StyleAt(startPos);
+ char s[10];
+
+ for (unsigned int i = startPos; i < endPos; 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_LOUT_WORD) {
+ if (ch == '@') {
+ for (unsigned int j = 0; j < 8; j++) {
+ if (!IsAWordChar(styler[i + j])) {
+ break;
+ }
+ s[j] = styler[i + j];
+ s[j + 1] = '\0';
+ }
+ if (strcmp(s, "@Begin") == 0) {
+ levelCurrent++;
+ } else if (strcmp(s, "@End") == 0) {
+ levelCurrent--;
+ }
+ }
+ } else if (style == SCE_LOUT_OPERATOR) {
+ if (ch == '{') {
+ levelCurrent++;
+ } else if (ch == '}') {
+ levelCurrent--;
+ }
+ }
+ if (atEOL) {
+ int lev = levelPrev;
+ if (visibleChars == 0 && foldCompact) {
+ 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 loutWordLists[] = {
+ "Predefined identifiers",
+ "Predefined delimiters",
+ "Predefined keywords",
+ 0,
+ };
+
+LexerModule lmLout(SCLEX_LOUT, ColouriseLoutDoc, "lout", FoldLoutDoc, loutWordLists);
diff --git a/src/LexLua.cpp b/src/LexLua.cpp
new file mode 100755
index 0000000..b3a46ad
--- /dev/null
+++ b/src/LexLua.cpp
@@ -0,0 +1,357 @@
+// Scintilla source code edit control
+/** @file LexLua.cxx
+ ** Lexer for Lua language.
+ **
+ ** Written by Paul Winwood.
+ ** Folder by Alexey Yutkin.
+ ** Modified by Marcos E. Wurzius & Philippe Lhoste
+ **/
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+// Extended to accept accented characters
+static inline bool IsAWordChar(int ch) {
+ return ch >= 0x80 ||
+ (isalnum(ch) || ch == '.' || ch == '_');
+}
+
+static inline bool IsAWordStart(int ch) {
+ return ch >= 0x80 ||
+ (isalpha(ch) || ch == '_');
+}
+
+static inline bool IsANumberChar(int ch) {
+ // Not exactly following number definition (several dots are seen as OK, etc.)
+ // but probably enough in most cases.
+ return (ch < 0x80) &&
+ (isdigit(ch) || toupper(ch) == 'E' ||
+ ch == '.' || ch == '-' || ch == '+' ||
+ (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'));
+}
+
+static inline bool IsLuaOperator(int ch) {
+ if (ch >= 0x80 || isalnum(ch)) {
+ return false;
+ }
+ // '.' left out as it is used to make up numbers
+ if (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;
+}
+
+// Test for [=[ ... ]=] delimiters, returns 0 if it's only a [ or ],
+// return 1 for [[ or ]], returns >=2 for [=[ or ]=] and so on.
+// The maximum number of '=' characters allowed is 254.
+static int LongDelimCheck(StyleContext &sc) {
+ int sep = 1;
+ while (sc.GetRelative(sep) == '=' && sep < 0xFF)
+ sep++;
+ if (sc.GetRelative(sep) == sc.ch)
+ return sep;
+ return 0;
+}
+
+static void ColouriseLuaDoc(
+ unsigned int startPos,
+ int 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];
+
+ int currentLine = styler.GetLine(startPos);
+ // Initialize long string [[ ... ]] or block comment --[[ ... ]] nesting level,
+ // if we are inside such a string. Block comment was introduced in Lua 5.0,
+ // blocks with separators [=[ ... ]=] in Lua 5.1.
+ int nestLevel = 0;
+ int sepCount = 0;
+ if (initStyle == SCE_LUA_LITERALSTRING || initStyle == SCE_LUA_COMMENT) {
+ int lineState = styler.GetLineState(currentLine - 1);
+ nestLevel = lineState >> 8;
+ sepCount = lineState & 0xFF;
+ }
+
+ // Do not leak onto next line
+ if (initStyle == SCE_LUA_STRINGEOL || initStyle == SCE_LUA_COMMENTLINE || initStyle == SCE_LUA_PREPROCESSOR) {
+ initStyle = SCE_LUA_DEFAULT;
+ }
+
+ StyleContext sc(startPos, length, initStyle, styler);
+ if (startPos == 0 && sc.ch == '#') {
+ // shbang line: # is a comment only if first char of the script
+ sc.SetState(SCE_LUA_COMMENTLINE);
+ }
+ for (; sc.More(); sc.Forward()) {
+ if (sc.atLineEnd) {
+ // Update the line state, so it can be seen by next line
+ currentLine = styler.GetLine(sc.currentPos);
+ switch (sc.state) {
+ case SCE_LUA_LITERALSTRING:
+ case SCE_LUA_COMMENT:
+ // Inside a literal string or block comment, we set the line state
+ styler.SetLineState(currentLine, (nestLevel << 8) | sepCount);
+ break;
+ default:
+ // Reset the line state
+ styler.SetLineState(currentLine, 0);
+ break;
+ }
+ }
+ if (sc.atLineStart && (sc.state == SCE_LUA_STRING)) {
+ // Prevent SCE_LUA_STRINGEOL from leaking back to previous line
+ sc.SetState(SCE_LUA_STRING);
+ }
+
+ // Handle string line continuation
+ if ((sc.state == SCE_LUA_STRING || sc.state == SCE_LUA_CHARACTER) &&
+ sc.ch == '\\') {
+ if (sc.chNext == '\n' || sc.chNext == '\r') {
+ sc.Forward();
+ if (sc.ch == '\r' && sc.chNext == '\n') {
+ sc.Forward();
+ }
+ continue;
+ }
+ }
+
+ // Determine if the current state should terminate.
+ if (sc.state == SCE_LUA_OPERATOR) {
+ sc.SetState(SCE_LUA_DEFAULT);
+ } else if (sc.state == SCE_LUA_NUMBER) {
+ // We stop the number definition on non-numerical non-dot non-eE non-sign non-hexdigit char
+ if (!IsANumberChar(sc.ch)) {
+ sc.SetState(SCE_LUA_DEFAULT);
+ }
+ } else if (sc.state == SCE_LUA_IDENTIFIER) {
+ if (!IsAWordChar(sc.ch) || sc.Match('.', '.')) {
+ char s[100];
+ sc.GetCurrent(s, sizeof(s));
+ if (keywords.InList(s)) {
+ sc.ChangeState(SCE_LUA_WORD);
+ } else if (keywords2.InList(s)) {
+ sc.ChangeState(SCE_LUA_WORD2);
+ } else if (keywords3.InList(s)) {
+ sc.ChangeState(SCE_LUA_WORD3);
+ } else if (keywords4.InList(s)) {
+ sc.ChangeState(SCE_LUA_WORD4);
+ } else if (keywords5.InList(s)) {
+ sc.ChangeState(SCE_LUA_WORD5);
+ } else if (keywords6.InList(s)) {
+ sc.ChangeState(SCE_LUA_WORD6);
+ } else if (keywords6.InList(s)) {
+ sc.ChangeState(SCE_LUA_WORD6);
+ } else if (keywords7.InList(s)) {
+ sc.ChangeState(SCE_LUA_WORD7);
+ } else if (keywords8.InList(s)) {
+ sc.ChangeState(SCE_LUA_WORD8);
+ }
+ sc.SetState(SCE_LUA_DEFAULT);
+ }
+ } else if (sc.state == SCE_LUA_COMMENTLINE || sc.state == SCE_LUA_PREPROCESSOR) {
+ if (sc.atLineEnd) {
+ sc.ForwardSetState(SCE_LUA_DEFAULT);
+ }
+ } else if (sc.state == SCE_LUA_STRING) {
+ if (sc.ch == '\\') {
+ if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
+ sc.Forward();
+ }
+ } else if (sc.ch == '\"') {
+ sc.ForwardSetState(SCE_LUA_DEFAULT);
+ } else if (sc.atLineEnd) {
+ sc.ChangeState(SCE_LUA_STRINGEOL);
+ sc.ForwardSetState(SCE_LUA_DEFAULT);
+ }
+ } else if (sc.state == SCE_LUA_CHARACTER) {
+ if (sc.ch == '\\') {
+ if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
+ sc.Forward();
+ }
+ } else if (sc.ch == '\'') {
+ sc.ForwardSetState(SCE_LUA_DEFAULT);
+ } else if (sc.atLineEnd) {
+ sc.ChangeState(SCE_LUA_STRINGEOL);
+ sc.ForwardSetState(SCE_LUA_DEFAULT);
+ }
+ } else if (sc.state == SCE_LUA_LITERALSTRING || sc.state == SCE_LUA_COMMENT) {
+ if (sc.ch == '[') {
+ int sep = LongDelimCheck(sc);
+ if (sep == 1 && sepCount == 1) { // [[-only allowed to nest
+ nestLevel++;
+ sc.Forward();
+ }
+ } else if (sc.ch == ']') {
+ int sep = LongDelimCheck(sc);
+ if (sep == 1 && sepCount == 1) { // un-nest with ]]-only
+ nestLevel--;
+ sc.Forward();
+ if (nestLevel == 0) {
+ sc.ForwardSetState(SCE_LUA_DEFAULT);
+ }
+ } else if (sep > 1 && sep == sepCount) { // ]=]-style delim
+ sc.Forward(sep);
+ sc.ForwardSetState(SCE_LUA_DEFAULT);
+ }
+ }
+ }
+
+ // Determine if a new state should be entered.
+ if (sc.state == SCE_LUA_DEFAULT) {
+ if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
+ sc.SetState(SCE_LUA_NUMBER);
+ if (sc.ch == '0' && toupper(sc.chNext) == 'X') {
+ sc.Forward(1);
+ }
+ } else if (IsAWordStart(sc.ch)) {
+ sc.SetState(SCE_LUA_IDENTIFIER);
+ } else if (sc.ch == '\"') {
+ sc.SetState(SCE_LUA_STRING);
+ } else if (sc.ch == '\'') {
+ sc.SetState(SCE_LUA_CHARACTER);
+ } else if (sc.ch == '[') {
+ sepCount = LongDelimCheck(sc);
+ if (sepCount == 0) {
+ sc.SetState(SCE_LUA_OPERATOR);
+ } else {
+ nestLevel = 1;
+ sc.SetState(SCE_LUA_LITERALSTRING);
+ sc.Forward(sepCount);
+ }
+ } else if (sc.Match('-', '-')) {
+ sc.SetState(SCE_LUA_COMMENTLINE);
+ if (sc.Match("--[")) {
+ sc.Forward(2);
+ sepCount = LongDelimCheck(sc);
+ if (sepCount > 0) {
+ nestLevel = 1;
+ sc.ChangeState(SCE_LUA_COMMENT);
+ sc.Forward(sepCount);
+ }
+ } else {
+ sc.Forward();
+ }
+ } else if (sc.atLineStart && sc.Match('$')) {
+ sc.SetState(SCE_LUA_PREPROCESSOR); // Obsolete since Lua 4.0, but still in old code
+ } else if (IsLuaOperator(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_LUA_OPERATOR);
+ }
+ }
+ }
+ sc.Complete();
+}
+
+static void FoldLuaDoc(unsigned int startPos, int length, int /* initStyle */, WordList *[],
+ Accessor &styler) {
+ unsigned int lengthDoc = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
+ int levelCurrent = levelPrev;
+ char chNext = styler[startPos];
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ int styleNext = styler.StyleAt(startPos);
+ char s[10];
+
+ for (unsigned int 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_LUA_WORD) {
+ if (ch == 'i' || ch == 'd' || ch == 'f' || ch == 'e' || ch == 'r' || ch == 'u') {
+ for (unsigned int j = 0; j < 8; j++) {
+ if (!iswordchar(styler[i + j])) {
+ break;
+ }
+ s[j] = styler[i + j];
+ s[j + 1] = '\0';
+ }
+
+ if ((strcmp(s, "if") == 0) || (strcmp(s, "do") == 0) || (strcmp(s, "function") == 0) || (strcmp(s, "repeat") == 0)) {
+ levelCurrent++;
+ }
+ if ((strcmp(s, "end") == 0) || (strcmp(s, "elseif") == 0) || (strcmp(s, "until") == 0)) {
+ levelCurrent--;
+ }
+ }
+ } else if (style == SCE_LUA_OPERATOR) {
+ if (ch == '{' || ch == '(') {
+ levelCurrent++;
+ } else if (ch == '}' || ch == ')') {
+ levelCurrent--;
+ }
+ } else if (style == SCE_LUA_LITERALSTRING || style == SCE_LUA_COMMENT) {
+ if (ch == '[') {
+ levelCurrent++;
+ } else if (ch == ']') {
+ levelCurrent--;
+ }
+ }
+
+ if (atEOL) {
+ int lev = levelPrev;
+ if (visibleChars == 0 && foldCompact) {
+ 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 luaWordListDesc[] = {
+ "Keywords",
+ "Basic functions",
+ "String, (table) & math functions",
+ "(coroutines), I/O & system facilities",
+ "user1",
+ "user2",
+ "user3",
+ "user4",
+ 0
+};
+
+LexerModule lmLua(SCLEX_LUA, ColouriseLuaDoc, "lua", FoldLuaDoc, luaWordListDesc);
diff --git a/src/LexMMIXAL.cpp b/src/LexMMIXAL.cpp
new file mode 100755
index 0000000..f447899
--- /dev/null
+++ b/src/LexMMIXAL.cpp
@@ -0,0 +1,183 @@
+// Scintilla source code edit control
+/** @file LexMMIXAL.cxx
+ ** Lexer for MMIX Assembler Language.
+ ** Written by Christoph Hösler <christoph.hoesler@student.uni-tuebingen.de>
+ ** For information about MMIX visit http://www-cs-faculty.stanford.edu/~knuth/mmix.html
+ **/
+// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+
+
+static inline bool IsAWordChar(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == ':' || ch == '_');
+}
+
+inline bool isMMIXALOperator(char ch) {
+ if (isalnum(ch))
+ return false;
+ if (ch == '+' || ch == '-' || ch == '|' || ch == '^' ||
+ ch == '*' || ch == '/' || ch == '/' ||
+ ch == '%' || ch == '<' || ch == '>' || ch == '&' ||
+ ch == '~' || ch == '$' ||
+ ch == ',' || ch == '(' || ch == ')' ||
+ ch == '[' || ch == ']')
+ return true;
+ return false;
+}
+
+static void ColouriseMMIXALDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+ Accessor &styler) {
+
+ WordList &opcodes = *keywordlists[0];
+ WordList &special_register = *keywordlists[1];
+ WordList &predef_symbols = *keywordlists[2];
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ for (; sc.More(); sc.Forward())
+ {
+ // No EOL continuation
+ if (sc.atLineStart) {
+ if (sc.ch == '@' && sc.chNext == 'i') {
+ sc.SetState(SCE_MMIXAL_INCLUDE);
+ } else {
+ sc.SetState(SCE_MMIXAL_LEADWS);
+ }
+ }
+
+ // Check if first non whitespace character in line is alphanumeric
+ if (sc.state == SCE_MMIXAL_LEADWS && !isspace(sc.ch)) { // LEADWS
+ if(!IsAWordChar(sc.ch)) {
+ sc.SetState(SCE_MMIXAL_COMMENT);
+ } else {
+ if(sc.atLineStart) {
+ sc.SetState(SCE_MMIXAL_LABEL);
+ } else {
+ sc.SetState(SCE_MMIXAL_OPCODE_PRE);
+ }
+ }
+ }
+
+ // Determine if the current state should terminate.
+ if (sc.state == SCE_MMIXAL_OPERATOR) { // OPERATOR
+ sc.SetState(SCE_MMIXAL_OPERANDS);
+ } else if (sc.state == SCE_MMIXAL_NUMBER) { // NUMBER
+ if (!isdigit(sc.ch)) {
+ if (IsAWordChar(sc.ch)) {
+ char s[100];
+ sc.GetCurrent(s, sizeof(s));
+ sc.ChangeState(SCE_MMIXAL_REF);
+ sc.SetState(SCE_MMIXAL_REF);
+ } else {
+ sc.SetState(SCE_MMIXAL_OPERANDS);
+ }
+ }
+ } else if (sc.state == SCE_MMIXAL_LABEL) { // LABEL
+ if (!IsAWordChar(sc.ch) ) {
+ sc.SetState(SCE_MMIXAL_OPCODE_PRE);
+ }
+ } else if (sc.state == SCE_MMIXAL_REF) { // REF
+ if (!IsAWordChar(sc.ch) ) {
+ char s[100];
+ sc.GetCurrent(s, sizeof(s));
+ if (*s == ':') { // ignore base prefix for match
+ for (size_t i = 0; i != sizeof(s); ++i) {
+ *(s+i) = *(s+i+1);
+ }
+ }
+ if (special_register.InList(s)) {
+ sc.ChangeState(SCE_MMIXAL_REGISTER);
+ } else if (predef_symbols.InList(s)) {
+ sc.ChangeState(SCE_MMIXAL_SYMBOL);
+ }
+ sc.SetState(SCE_MMIXAL_OPERANDS);
+ }
+ } else if (sc.state == SCE_MMIXAL_OPCODE_PRE) { // OPCODE_PRE
+ if (!isspace(sc.ch)) {
+ sc.SetState(SCE_MMIXAL_OPCODE);
+ }
+ } else if (sc.state == SCE_MMIXAL_OPCODE) { // OPCODE
+ if (!IsAWordChar(sc.ch) ) {
+ char s[100];
+ sc.GetCurrent(s, sizeof(s));
+ if (opcodes.InList(s)) {
+ sc.ChangeState(SCE_MMIXAL_OPCODE_VALID);
+ } else {
+ sc.ChangeState(SCE_MMIXAL_OPCODE_UNKNOWN);
+ }
+ sc.SetState(SCE_MMIXAL_OPCODE_POST);
+ }
+ } else if (sc.state == SCE_MMIXAL_STRING) { // STRING
+ if (sc.ch == '\"') {
+ sc.ForwardSetState(SCE_MMIXAL_OPERANDS);
+ } else if (sc.atLineEnd) {
+ sc.ForwardSetState(SCE_MMIXAL_OPERANDS);
+ }
+ } else if (sc.state == SCE_MMIXAL_CHAR) { // CHAR
+ if (sc.ch == '\'') {
+ sc.ForwardSetState(SCE_MMIXAL_OPERANDS);
+ } else if (sc.atLineEnd) {
+ sc.ForwardSetState(SCE_MMIXAL_OPERANDS);
+ }
+ } else if (sc.state == SCE_MMIXAL_REGISTER) { // REGISTER
+ if (!isdigit(sc.ch)) {
+ sc.SetState(SCE_MMIXAL_OPERANDS);
+ }
+ } else if (sc.state == SCE_MMIXAL_HEX) { // HEX
+ if (!isxdigit(sc.ch)) {
+ sc.SetState(SCE_MMIXAL_OPERANDS);
+ }
+ }
+
+ // Determine if a new state should be entered.
+ if (sc.state == SCE_MMIXAL_OPCODE_POST || // OPCODE_POST
+ sc.state == SCE_MMIXAL_OPERANDS) { // OPERANDS
+ if (sc.state == SCE_MMIXAL_OPERANDS && isspace(sc.ch)) {
+ if (!sc.atLineEnd) {
+ sc.SetState(SCE_MMIXAL_COMMENT);
+ }
+ } else if (isdigit(sc.ch)) {
+ sc.SetState(SCE_MMIXAL_NUMBER);
+ } else if (IsAWordChar(sc.ch) || sc.Match('@')) {
+ sc.SetState(SCE_MMIXAL_REF);
+ } else if (sc.Match('\"')) {
+ sc.SetState(SCE_MMIXAL_STRING);
+ } else if (sc.Match('\'')) {
+ sc.SetState(SCE_MMIXAL_CHAR);
+ } else if (sc.Match('$')) {
+ sc.SetState(SCE_MMIXAL_REGISTER);
+ } else if (sc.Match('#')) {
+ sc.SetState(SCE_MMIXAL_HEX);
+ } else if (isMMIXALOperator(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_MMIXAL_OPERATOR);
+ }
+ }
+ }
+ sc.Complete();
+}
+
+static const char * const MMIXALWordListDesc[] = {
+ "Operation Codes",
+ "Special Register",
+ "Predefined Symbols",
+ 0
+};
+
+LexerModule lmMMIXAL(SCLEX_MMIXAL, ColouriseMMIXALDoc, "mmixal", 0, MMIXALWordListDesc);
+
diff --git a/src/LexMPT.cpp b/src/LexMPT.cpp
new file mode 100755
index 0000000..1058b9b
--- /dev/null
+++ b/src/LexMPT.cpp
@@ -0,0 +1,182 @@
+// Scintilla source code edit control
+/** @file LexMPT.cxx
+ ** Lexer for MPT specific files. Based on LexOthers.cxx
+ ** LOT = the text log file created by the MPT application while running a test program
+ ** Other MPT specific files to be added later.
+ **/
+// Copyright 2003 by Marius Gheorghe <mgheorghe@cabletest.com>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+#include "SString.h"
+
+static int GetLotLineState(SString &line) {
+ if (line.length()) {
+ // Most of the time the first non-blank character in line determines that line's type
+ // Now finds the first non-blank character
+ unsigned i; // Declares counter here to make it persistent after the for loop
+ for (i = 0; i < line.length(); ++i) {
+ if (!isspace(line[i]))
+ break;
+ }
+
+ // Checks if it was a blank line
+ if (i == line.length())
+ return SCE_LOT_DEFAULT;
+
+ switch (line[i]) {
+ case '*': // Fail measurement
+ return SCE_LOT_FAIL;
+
+ case '+': // Header
+ case '|': // Header
+ return SCE_LOT_HEADER;
+
+ case ':': // Set test limits
+ return SCE_LOT_SET;
+
+ case '-': // Section break
+ return SCE_LOT_BREAK;
+
+ default: // Any other line
+ // Checks for message at the end of lot file
+ if (line.contains("PASSED")) {
+ return SCE_LOT_PASS;
+ }
+ else if (line.contains("FAILED")) {
+ return SCE_LOT_FAIL;
+ }
+ else if (line.contains("ABORTED")) {
+ return SCE_LOT_ABORT;
+ }
+ else {
+ return i ? SCE_LOT_PASS : SCE_LOT_DEFAULT;
+ }
+ }
+ }
+ else {
+ return SCE_LOT_DEFAULT;
+ }
+}
+
+static void ColourizeLotDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
+ styler.StartAt(startPos);
+ styler.StartSegment(startPos);
+ bool atLineStart = true;// Arms the 'at line start' flag
+ char chNext = styler.SafeGetCharAt(startPos);
+ SString line("");
+ line.setsizegrowth(256); // Lot lines are less than 256 chars long most of the time. This should avoid reallocations
+
+ // Styles LOT document
+ unsigned int i; // Declared here because it's used after the for loop
+ for (i = startPos; i < startPos + length; ++i) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ line += ch;
+ atLineStart = false;
+
+ // LOT files are only used on the Win32 platform, thus EOL == CR+LF
+ // Searches for the end of line
+ if (ch == '\r' && chNext == '\n') {
+ line += chNext; // Gets the '\n'
+ ++i; // Advances past the '\n'
+ chNext = styler.SafeGetCharAt(i + 1); // Gets character of next line
+ styler.ColourTo(i, GetLotLineState(line));
+ line = "";
+ atLineStart = true; // Arms flag for next line
+ }
+ }
+
+ // Last line may not have a line ending
+ if (!atLineStart) {
+ styler.ColourTo(i - 1, GetLotLineState(line));
+ }
+}
+
+// Folds an MPT LOT file: the blocks that can be folded are:
+// sections (headed by a set line)
+// passes (contiguous pass results within a section)
+// fails (contiguous fail results within a section)
+static void FoldLotDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 0) != 0;
+ unsigned int endPos = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+
+ char chNext = styler.SafeGetCharAt(startPos);
+ int style = SCE_LOT_DEFAULT;
+ int styleNext = styler.StyleAt(startPos);
+ int lev = SC_FOLDLEVELBASE;
+
+ // Gets style of previous line if not at the beginning of the document
+ if (startPos > 1)
+ style = styler.StyleAt(startPos - 2);
+
+ for (unsigned int i = startPos; i < endPos; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+
+ if (ch == '\r' && chNext == '\n') {
+ // TO DO:
+ // Should really get the state of the previous line from the styler
+ int stylePrev = style;
+ style = styleNext;
+ styleNext = styler.StyleAt(i + 2);
+
+ switch (style) {
+/*
+ case SCE_LOT_SET:
+ lev = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG;
+ break;
+*/
+ case SCE_LOT_FAIL:
+/*
+ if (stylePrev != SCE_LOT_FAIL)
+ lev = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG;
+ else
+ lev = SC_FOLDLEVELBASE + 1;
+*/
+ lev = SC_FOLDLEVELBASE;
+ break;
+
+ default:
+ if (lineCurrent == 0 || stylePrev == SCE_LOT_FAIL)
+ lev = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG;
+ else
+ lev = SC_FOLDLEVELBASE + 1;
+
+ if (visibleChars == 0 && foldCompact)
+ lev |= SC_FOLDLEVELWHITEFLAG;
+ break;
+ }
+
+ if (lev != styler.LevelAt(lineCurrent))
+ styler.SetLevel(lineCurrent, lev);
+
+ lineCurrent++;
+ visibleChars = 0;
+ }
+
+ if (!isspacechar(ch))
+ visibleChars++;
+ }
+
+ int flagsNext = styler.LevelAt(lineCurrent) & ~SC_FOLDLEVELNUMBERMASK;
+ styler.SetLevel(lineCurrent, lev | flagsNext);
+}
+
+static const char * const emptyWordListDesc[] = {
+ 0
+};
+
+LexerModule lmLot(SCLEX_LOT, ColourizeLotDoc, "lot", FoldLotDoc, emptyWordListDesc);
diff --git a/src/LexMSSQL.cpp b/src/LexMSSQL.cpp
new file mode 100755
index 0000000..a946668
--- /dev/null
+++ b/src/LexMSSQL.cpp
@@ -0,0 +1,359 @@
+// Scintilla source code edit control
+/** @file LexMSSQL.cxx
+ ** Lexer for MSSQL.
+ **/
+// By Filip Yaghob <fyaghob@gmail.com>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+#define KW_MSSQL_STATEMENTS 0
+#define KW_MSSQL_DATA_TYPES 1
+#define KW_MSSQL_SYSTEM_TABLES 2
+#define KW_MSSQL_GLOBAL_VARIABLES 3
+#define KW_MSSQL_FUNCTIONS 4
+#define KW_MSSQL_STORED_PROCEDURES 5
+#define KW_MSSQL_OPERATORS 6
+
+static bool isMSSQLOperator(char ch) {
+ if (isascii(ch) && isalnum(ch))
+ return false;
+ // '.' left out as it is used to make up numbers
+ if (ch == '%' || ch == '^' || ch == '&' || ch == '*' ||
+ ch == '-' || ch == '+' || ch == '=' || ch == '|' ||
+ ch == '<' || ch == '>' || ch == '/' ||
+ ch == '!' || ch == '~' || ch == '(' || ch == ')' ||
+ ch == ',')
+ return true;
+ return false;
+}
+
+static char classifyWordSQL(unsigned int start,
+ unsigned int end,
+ WordList *keywordlists[],
+ Accessor &styler,
+ unsigned int actualState,
+ unsigned int prevState) {
+ char s[256];
+ bool wordIsNumber = isdigit(styler[start]) || (styler[start] == '.');
+
+ WordList &kwStatements = *keywordlists[KW_MSSQL_STATEMENTS];
+ WordList &kwDataTypes = *keywordlists[KW_MSSQL_DATA_TYPES];
+ WordList &kwSystemTables = *keywordlists[KW_MSSQL_SYSTEM_TABLES];
+ WordList &kwGlobalVariables = *keywordlists[KW_MSSQL_GLOBAL_VARIABLES];
+ WordList &kwFunctions = *keywordlists[KW_MSSQL_FUNCTIONS];
+ WordList &kwStoredProcedures = *keywordlists[KW_MSSQL_STORED_PROCEDURES];
+ WordList &kwOperators = *keywordlists[KW_MSSQL_OPERATORS];
+
+ for (unsigned int i = 0; i < end - start + 1 && i < 128; i++) {
+ s[i] = static_cast<char>(tolower(styler[start + i]));
+ s[i + 1] = '\0';
+ }
+ char chAttr = SCE_MSSQL_IDENTIFIER;
+
+ if (actualState == SCE_MSSQL_GLOBAL_VARIABLE) {
+
+ if (kwGlobalVariables.InList(&s[2]))
+ chAttr = SCE_MSSQL_GLOBAL_VARIABLE;
+
+ } else if (wordIsNumber) {
+ chAttr = SCE_MSSQL_NUMBER;
+
+ } else if (prevState == SCE_MSSQL_DEFAULT_PREF_DATATYPE) {
+ // Look first in datatypes
+ if (kwDataTypes.InList(s))
+ chAttr = SCE_MSSQL_DATATYPE;
+ else if (kwOperators.InList(s))
+ chAttr = SCE_MSSQL_OPERATOR;
+ else if (kwStatements.InList(s))
+ chAttr = SCE_MSSQL_STATEMENT;
+ else if (kwSystemTables.InList(s))
+ chAttr = SCE_MSSQL_SYSTABLE;
+ else if (kwFunctions.InList(s))
+ chAttr = SCE_MSSQL_FUNCTION;
+ else if (kwStoredProcedures.InList(s))
+ chAttr = SCE_MSSQL_STORED_PROCEDURE;
+
+ } else {
+ if (kwOperators.InList(s))
+ chAttr = SCE_MSSQL_OPERATOR;
+ else if (kwStatements.InList(s))
+ chAttr = SCE_MSSQL_STATEMENT;
+ else if (kwSystemTables.InList(s))
+ chAttr = SCE_MSSQL_SYSTABLE;
+ else if (kwFunctions.InList(s))
+ chAttr = SCE_MSSQL_FUNCTION;
+ else if (kwStoredProcedures.InList(s))
+ chAttr = SCE_MSSQL_STORED_PROCEDURE;
+ else if (kwDataTypes.InList(s))
+ chAttr = SCE_MSSQL_DATATYPE;
+ }
+
+ styler.ColourTo(end, chAttr);
+
+ return chAttr;
+}
+
+static void ColouriseMSSQLDoc(unsigned int startPos, int length,
+ int initStyle, WordList *keywordlists[], Accessor &styler) {
+
+
+ styler.StartAt(startPos);
+
+ bool fold = styler.GetPropertyInt("fold") != 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int spaceFlags = 0;
+
+ int state = initStyle;
+ int prevState = initStyle;
+ char chPrev = ' ';
+ char chNext = styler[startPos];
+ styler.StartSegment(startPos);
+ unsigned int lengthDoc = startPos + length;
+ for (unsigned int i = startPos; i < lengthDoc; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+
+ if ((ch == '\r' && chNext != '\n') || (ch == '\n')) {
+ int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags);
+ int lev = indentCurrent;
+ if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
+ // Only non whitespace lines can be headers
+ int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags);
+ if (indentCurrent < (indentNext & ~SC_FOLDLEVELWHITEFLAG)) {
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ }
+ }
+ if (fold) {
+ styler.SetLevel(lineCurrent, lev);
+ }
+ }
+
+ if (styler.IsLeadByte(ch)) {
+ chNext = styler.SafeGetCharAt(i + 2);
+ chPrev = ' ';
+ i += 1;
+ continue;
+ }
+
+ // When the last char isn't part of the state (have to deal with it too)...
+ if ( (state == SCE_MSSQL_IDENTIFIER) ||
+ (state == SCE_MSSQL_STORED_PROCEDURE) ||
+ (state == SCE_MSSQL_DATATYPE) ||
+ //~ (state == SCE_MSSQL_COLUMN_NAME) ||
+ (state == SCE_MSSQL_FUNCTION) ||
+ //~ (state == SCE_MSSQL_GLOBAL_VARIABLE) ||
+ (state == SCE_MSSQL_VARIABLE)) {
+ if (!iswordchar(ch)) {
+ int stateTmp;
+
+ if ((state == SCE_MSSQL_VARIABLE) || (state == SCE_MSSQL_COLUMN_NAME)) {
+ styler.ColourTo(i - 1, state);
+ stateTmp = state;
+ } else
+ stateTmp = classifyWordSQL(styler.GetStartSegment(), i - 1, keywordlists, styler, state, prevState);
+
+ prevState = state;
+
+ if (stateTmp == SCE_MSSQL_IDENTIFIER || stateTmp == SCE_MSSQL_VARIABLE)
+ state = SCE_MSSQL_DEFAULT_PREF_DATATYPE;
+ else
+ state = SCE_MSSQL_DEFAULT;
+ }
+ } else if (state == SCE_MSSQL_LINE_COMMENT) {
+ if (ch == '\r' || ch == '\n') {
+ styler.ColourTo(i - 1, state);
+ prevState = state;
+ state = SCE_MSSQL_DEFAULT;
+ }
+ } else if (state == SCE_MSSQL_GLOBAL_VARIABLE) {
+ if ((ch != '@') && !iswordchar(ch)) {
+ classifyWordSQL(styler.GetStartSegment(), i - 1, keywordlists, styler, state, prevState);
+ prevState = state;
+ state = SCE_MSSQL_DEFAULT;
+ }
+ }
+
+ // If is the default or one of the above succeeded
+ if (state == SCE_MSSQL_DEFAULT || state == SCE_MSSQL_DEFAULT_PREF_DATATYPE) {
+ if (iswordstart(ch)) {
+ styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
+ prevState = state;
+ state = SCE_MSSQL_IDENTIFIER;
+ } else if (ch == '/' && chNext == '*') {
+ styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
+ prevState = state;
+ state = SCE_MSSQL_COMMENT;
+ } else if (ch == '-' && chNext == '-') {
+ styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
+ prevState = state;
+ state = SCE_MSSQL_LINE_COMMENT;
+ } else if (ch == '\'') {
+ styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
+ prevState = state;
+ state = SCE_MSSQL_STRING;
+ } else if (ch == '"') {
+ styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
+ prevState = state;
+ state = SCE_MSSQL_COLUMN_NAME;
+ } else if (ch == '[') {
+ styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
+ prevState = state;
+ state = SCE_MSSQL_COLUMN_NAME_2;
+ } else if (isMSSQLOperator(ch)) {
+ styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
+ styler.ColourTo(i, SCE_MSSQL_OPERATOR);
+ //~ style = SCE_MSSQL_DEFAULT;
+ prevState = state;
+ state = SCE_MSSQL_DEFAULT;
+ } else if (ch == '@') {
+ styler.ColourTo(i - 1, SCE_MSSQL_DEFAULT);
+ prevState = state;
+ if (chNext == '@') {
+ state = SCE_MSSQL_GLOBAL_VARIABLE;
+// i += 2;
+ } else
+ state = SCE_MSSQL_VARIABLE;
+ }
+
+
+ // When the last char is part of the state...
+ } else if (state == SCE_MSSQL_COMMENT) {
+ if (ch == '/' && chPrev == '*') {
+ if (((i > (styler.GetStartSegment() + 2)) || ((initStyle == SCE_MSSQL_COMMENT) &&
+ (styler.GetStartSegment() == startPos)))) {
+ styler.ColourTo(i, state);
+ //~ state = SCE_MSSQL_COMMENT;
+ prevState = state;
+ state = SCE_MSSQL_DEFAULT;
+ }
+ }
+ } else if (state == SCE_MSSQL_STRING) {
+ if (ch == '\'') {
+ if ( chNext == '\'' ) {
+ i++;
+ ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else {
+ styler.ColourTo(i, state);
+ prevState = state;
+ state = SCE_MSSQL_DEFAULT;
+ //i++;
+ }
+ //ch = chNext;
+ //chNext = styler.SafeGetCharAt(i + 1);
+ }
+ } else if (state == SCE_MSSQL_COLUMN_NAME) {
+ if (ch == '"') {
+ if (chNext == '"') {
+ i++;
+ ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else {
+ styler.ColourTo(i, state);
+ prevState = state;
+ state = SCE_MSSQL_DEFAULT_PREF_DATATYPE;
+ //i++;
+ }
+ }
+ } else if (state == SCE_MSSQL_COLUMN_NAME_2) {
+ if (ch == ']') {
+ styler.ColourTo(i, state);
+ prevState = state;
+ state = SCE_MSSQL_DEFAULT_PREF_DATATYPE;
+ //i++;
+ }
+ }
+
+ chPrev = ch;
+ }
+ styler.ColourTo(lengthDoc - 1, state);
+}
+
+static void FoldMSSQLDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
+ bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ unsigned int endPos = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
+ int levelCurrent = levelPrev;
+ char chNext = styler[startPos];
+ bool inComment = (styler.StyleAt(startPos-1) == SCE_MSSQL_COMMENT);
+ char s[10];
+ for (unsigned int i = startPos; i < endPos; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ int style = styler.StyleAt(i);
+ bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
+ // Comment folding
+ if (foldComment) {
+ if (!inComment && (style == SCE_MSSQL_COMMENT))
+ levelCurrent++;
+ else if (inComment && (style != SCE_MSSQL_COMMENT))
+ levelCurrent--;
+ inComment = (style == SCE_MSSQL_COMMENT);
+ }
+ if (style == SCE_MSSQL_STATEMENT) {
+ // Folding between begin and end
+ if (ch == 'b' || ch == 'e') {
+ for (unsigned int j = 0; j < 5; j++) {
+ if (!iswordchar(styler[i + j])) {
+ break;
+ }
+ s[j] = styler[i + j];
+ s[j + 1] = '\0';
+ }
+ if (strcmp(s, "begin") == 0) {
+ levelCurrent++;
+ }
+ if (strcmp(s, "end") == 0) {
+ levelCurrent--;
+ }
+ }
+ }
+ if (atEOL) {
+ int lev = levelPrev;
+ if (visibleChars == 0 && foldCompact)
+ 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 sqlWordListDesc[] = {
+ "Statements",
+ "Data Types",
+ "System tables",
+ "Global variables",
+ "Functions",
+ "System Stored Procedures",
+ "Operators",
+ 0,
+};
+
+LexerModule lmMSSQL(SCLEX_MSSQL, ColouriseMSSQLDoc, "mssql", FoldMSSQLDoc, sqlWordListDesc);
diff --git a/src/LexMatlab.cpp b/src/LexMatlab.cpp
new file mode 100755
index 0000000..249fa67
--- /dev/null
+++ b/src/LexMatlab.cpp
@@ -0,0 +1,225 @@
+// Scintilla source code edit control
+/** @file LexMatlab.cxx
+ ** Lexer for Matlab.
+ ** Written by José Fonseca
+ **
+ ** Changes by Christoph Dalitz 2003/12/04:
+ ** - added support for Octave
+ ** - Strings can now be included both in single or double quotes
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+
+static bool IsMatlabCommentChar(int c) {
+ return (c == '%') ;
+}
+
+static bool IsOctaveCommentChar(int c) {
+ return (c == '%' || c == '#') ;
+}
+
+static bool IsMatlabComment(Accessor &styler, int pos, int len) {
+ return len > 0 && IsMatlabCommentChar(styler[pos]) ;
+}
+
+static bool IsOctaveComment(Accessor &styler, int pos, int len) {
+ return len > 0 && IsOctaveCommentChar(styler[pos]) ;
+}
+
+static inline bool IsAWordChar(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '_');
+}
+
+static inline bool IsAWordStart(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '_');
+}
+
+static void ColouriseMatlabOctaveDoc(
+ unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler,
+ bool (*IsCommentChar)(int)) {
+
+ WordList &keywords = *keywordlists[0];
+
+ styler.StartAt(startPos);
+
+ bool transpose = false;
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ for (; sc.More(); sc.Forward()) {
+
+ if (sc.state == SCE_MATLAB_OPERATOR) {
+ if (sc.chPrev == '.') {
+ if (sc.ch == '*' || sc.ch == '/' || sc.ch == '\\' || sc.ch == '^') {
+ sc.ForwardSetState(SCE_MATLAB_DEFAULT);
+ transpose = false;
+ } else if (sc.ch == '\'') {
+ sc.ForwardSetState(SCE_MATLAB_DEFAULT);
+ transpose = true;
+ } else {
+ sc.SetState(SCE_MATLAB_DEFAULT);
+ }
+ } else {
+ sc.SetState(SCE_MATLAB_DEFAULT);
+ }
+ } else if (sc.state == SCE_MATLAB_KEYWORD) {
+ if (!isalnum(sc.ch) && sc.ch != '_') {
+ char s[100];
+ sc.GetCurrentLowered(s, sizeof(s));
+ if (keywords.InList(s)) {
+ sc.SetState(SCE_MATLAB_DEFAULT);
+ transpose = false;
+ } else {
+ sc.ChangeState(SCE_MATLAB_IDENTIFIER);
+ sc.SetState(SCE_MATLAB_DEFAULT);
+ transpose = true;
+ }
+ }
+ } else if (sc.state == SCE_MATLAB_NUMBER) {
+ if (!isdigit(sc.ch) && sc.ch != '.'
+ && !(sc.ch == 'e' || sc.ch == 'E')
+ && !((sc.ch == '+' || sc.ch == '-') && (sc.chPrev == 'e' || sc.chPrev == 'E'))) {
+ sc.SetState(SCE_MATLAB_DEFAULT);
+ transpose = true;
+ }
+ } else if (sc.state == SCE_MATLAB_STRING) {
+ if (sc.ch == '\'' && sc.chPrev != '\\') {
+ sc.ForwardSetState(SCE_MATLAB_DEFAULT);
+ }
+ } else if (sc.state == SCE_MATLAB_DOUBLEQUOTESTRING) {
+ if (sc.ch == '"' && sc.chPrev != '\\') {
+ sc.ForwardSetState(SCE_MATLAB_DEFAULT);
+ }
+ } else if (sc.state == SCE_MATLAB_COMMENT || sc.state == SCE_MATLAB_COMMAND) {
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_MATLAB_DEFAULT);
+ transpose = false;
+ }
+ }
+
+ if (sc.state == SCE_MATLAB_DEFAULT) {
+ if (IsCommentChar(sc.ch)) {
+ sc.SetState(SCE_MATLAB_COMMENT);
+ } else if (sc.ch == '!') {
+ sc.SetState(SCE_MATLAB_COMMAND);
+ } else if (sc.ch == '\'') {
+ if (transpose) {
+ sc.SetState(SCE_MATLAB_OPERATOR);
+ } else {
+ sc.SetState(SCE_MATLAB_STRING);
+ }
+ } else if (sc.ch == '"') {
+ sc.SetState(SCE_MATLAB_DOUBLEQUOTESTRING);
+ } else if (isdigit(sc.ch) || (sc.ch == '.' && isdigit(sc.chNext))) {
+ sc.SetState(SCE_MATLAB_NUMBER);
+ } else if (isalpha(sc.ch)) {
+ sc.SetState(SCE_MATLAB_KEYWORD);
+ } else if (isoperator(static_cast<char>(sc.ch)) || sc.ch == '@' || sc.ch == '\\') {
+ if (sc.ch == ')' || sc.ch == ']') {
+ transpose = true;
+ } else {
+ transpose = false;
+ }
+ sc.SetState(SCE_MATLAB_OPERATOR);
+ } else {
+ transpose = false;
+ }
+ }
+ }
+ sc.Complete();
+}
+
+static void ColouriseMatlabDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler) {
+ ColouriseMatlabOctaveDoc(startPos, length, initStyle, keywordlists, styler, IsMatlabCommentChar);
+}
+
+static void ColouriseOctaveDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler) {
+ ColouriseMatlabOctaveDoc(startPos, length, initStyle, keywordlists, styler, IsOctaveCommentChar);
+}
+
+static void FoldMatlabOctaveDoc(unsigned int startPos, int length, int,
+ WordList *[], Accessor &styler,
+ bool (*IsComment)(Accessor&,int,int)) {
+
+ int endPos = startPos + length;
+
+ // Backtrack to previous line in case need to fix its fold status
+ int lineCurrent = styler.GetLine(startPos);
+ if (startPos > 0) {
+ if (lineCurrent > 0) {
+ lineCurrent--;
+ startPos = styler.LineStart(lineCurrent);
+ }
+ }
+ int spaceFlags = 0;
+ int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, IsComment);
+ char chNext = styler[startPos];
+ for (int i = startPos; i < endPos; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+
+ if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == endPos)) {
+ int lev = indentCurrent;
+ int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags, IsComment);
+ if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
+ // Only non whitespace lines can be headers
+ if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) {
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ } else if (indentNext & SC_FOLDLEVELWHITEFLAG) {
+ // Line after is blank so check the next - maybe should continue further?
+ int spaceFlags2 = 0;
+ int indentNext2 = styler.IndentAmount(lineCurrent + 2, &spaceFlags2, IsComment);
+ if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext2 & SC_FOLDLEVELNUMBERMASK)) {
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ }
+ }
+ }
+ indentCurrent = indentNext;
+ styler.SetLevel(lineCurrent, lev);
+ lineCurrent++;
+ }
+ }
+}
+
+static void FoldMatlabDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler) {
+ FoldMatlabOctaveDoc(startPos, length, initStyle, keywordlists, styler, IsMatlabComment);
+}
+
+static void FoldOctaveDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler) {
+ FoldMatlabOctaveDoc(startPos, length, initStyle, keywordlists, styler, IsOctaveComment);
+}
+
+static const char * const matlabWordListDesc[] = {
+ "Keywords",
+ 0
+};
+
+static const char * const octaveWordListDesc[] = {
+ "Keywords",
+ 0
+};
+
+LexerModule lmMatlab(SCLEX_MATLAB, ColouriseMatlabDoc, "matlab", FoldMatlabDoc, matlabWordListDesc);
+
+LexerModule lmOctave(SCLEX_OCTAVE, ColouriseOctaveDoc, "octave", FoldOctaveDoc, octaveWordListDesc);
diff --git a/src/LexMetapost.cpp b/src/LexMetapost.cpp
new file mode 100755
index 0000000..b7d482c
--- /dev/null
+++ b/src/LexMetapost.cpp
@@ -0,0 +1,320 @@
+// Scintilla source code edit control
+
+// File: LexMetapost.cxx - general context conformant metapost coloring scheme
+// Author: Hans Hagen - PRAGMA ADE - Hasselt NL - www.pragma-ade.com
+// Version: September 28, 2003
+
+// Copyright: 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+// This lexer is derived from the one written for the texwork environment (1999++) which in
+// turn is inspired on texedit (1991++) which finds its roots in wdt (1986).
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+#include "StyleContext.h"
+
+// val SCE_METAPOST_DEFAULT = 0
+// val SCE_METAPOST_SPECIAL = 1
+// val SCE_METAPOST_GROUP = 2
+// val SCE_METAPOST_SYMBOL = 3
+// val SCE_METAPOST_COMMAND = 4
+// val SCE_METAPOST_TEXT = 5
+
+// Definitions in SciTEGlobal.properties:
+//
+// Metapost Highlighting
+//
+// # Default
+// style.metapost.0=fore:#7F7F00
+// # Special
+// style.metapost.1=fore:#007F7F
+// # Group
+// style.metapost.2=fore:#880000
+// # Symbol
+// style.metapost.3=fore:#7F7F00
+// # Command
+// style.metapost.4=fore:#008800
+// # Text
+// style.metapost.5=fore:#000000
+
+// lexer.tex.comment.process=0
+
+// Auxiliary functions:
+
+static inline bool endOfLine(Accessor &styler, unsigned int i) {
+ return
+ (styler[i] == '\n') || ((styler[i] == '\r') && (styler.SafeGetCharAt(i + 1) != '\n')) ;
+}
+
+static inline bool isMETAPOSTcomment(int ch) {
+ return
+ (ch == '%') ;
+}
+
+static inline bool isMETAPOSTone(int ch) {
+ return
+ (ch == '[') || (ch == ']') || (ch == '(') || (ch == ')') ||
+ (ch == ':') || (ch == '=') || (ch == '<') || (ch == '>') ||
+ (ch == '{') || (ch == '}') || (ch == '\'') || (ch == '\"') ;
+}
+
+static inline bool isMETAPOSTtwo(int ch) {
+ return
+ (ch == ';') || (ch == '$') || (ch == '@') || (ch == '#');
+}
+
+static inline bool isMETAPOSTthree(int ch) {
+ return
+ (ch == '.') || (ch == '-') || (ch == '+') || (ch == '/') ||
+ (ch == '*') || (ch == ',') || (ch == '|') || (ch == '`') ||
+ (ch == '!') || (ch == '?') || (ch == '^') || (ch == '&') ||
+ (ch == '%') ;
+}
+
+static inline bool isMETAPOSTidentifier(int ch) {
+ return
+ ((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) ||
+ (ch == '_') ;
+}
+
+static inline bool isMETAPOSTnumber(int ch) {
+ return
+ (ch >= '0') && (ch <= '9') ;
+}
+
+static inline bool isMETAPOSTstring(int ch) {
+ return
+ (ch == '\"') ;
+}
+
+static inline bool isMETAPOSTcolon(int ch) {
+ return
+ (ch == ':') ;
+}
+
+static inline bool isMETAPOSTequal(int ch) {
+ return
+ (ch == '=') ;
+}
+
+static int CheckMETAPOSTInterface(
+ unsigned int startPos,
+ int length,
+ Accessor &styler,
+ int defaultInterface) {
+
+ char lineBuffer[1024] ;
+ unsigned int linePos = 0 ;
+
+ // some day we can make something lexer.metapost.mapping=(none,0)(metapost,1)(mp,1)(metafun,2)...
+
+ if (styler.SafeGetCharAt(0) == '%') {
+ for (unsigned int i = 0; i < startPos + length; i++) {
+ lineBuffer[linePos++] = styler.SafeGetCharAt(i) ;
+ if (endOfLine(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) {
+ lineBuffer[linePos] = '\0';
+ if (strstr(lineBuffer, "interface=none")) {
+ return 0 ;
+ } else if (strstr(lineBuffer, "interface=metapost") || strstr(lineBuffer, "interface=mp")) {
+ return 1 ;
+ } else if (strstr(lineBuffer, "interface=metafun")) {
+ return 2 ;
+ } else if (styler.SafeGetCharAt(1) == 'D' && strstr(lineBuffer, "%D \\module")) {
+ // better would be to limit the search to just one line
+ return 2 ;
+ } else {
+ return defaultInterface ;
+ }
+ }
+ }
+ }
+
+ return defaultInterface ;
+}
+
+static void ColouriseMETAPOSTDoc(
+ unsigned int startPos,
+ int length,
+ int,
+ WordList *keywordlists[],
+ Accessor &styler) {
+
+ styler.StartAt(startPos) ;
+ styler.StartSegment(startPos) ;
+
+ bool processComment = styler.GetPropertyInt("lexer.metapost.comment.process", 0) == 1 ;
+ int defaultInterface = styler.GetPropertyInt("lexer.metapost.interface.default", 1) ;
+
+ int currentInterface = CheckMETAPOSTInterface(startPos,length,styler,defaultInterface) ;
+
+ // 0 no keyword highlighting
+ // 1 metapost keyword hightlighting
+ // 2+ metafun keyword hightlighting
+
+ int extraInterface = 0 ;
+
+ if (currentInterface != 0) {
+ extraInterface = currentInterface ;
+ }
+
+ WordList &keywords = *keywordlists[0] ;
+ WordList &keywords2 = *keywordlists[extraInterface-1] ;
+
+ StyleContext sc(startPos, length, SCE_METAPOST_TEXT, styler) ;
+
+ char key[100] ;
+
+ bool inTeX = false ;
+ bool inComment = false ;
+ bool inString = false ;
+ bool inClause = false ;
+
+ bool going = sc.More() ; // needed because of a fuzzy end of file state
+
+ for (; going; sc.Forward()) {
+
+ if (! sc.More()) { going = false ; } // we need to go one behind the end of text
+
+ if (inClause) {
+ sc.SetState(SCE_METAPOST_TEXT) ;
+ inClause = false ;
+ }
+
+ if (inComment) {
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_METAPOST_TEXT) ;
+ inTeX = false ;
+ inComment = false ;
+ inClause = false ;
+ inString = false ; // not correct but we want to stimulate one-lines
+ }
+ } else if (inString) {
+ if (isMETAPOSTstring(sc.ch)) {
+ sc.SetState(SCE_METAPOST_SPECIAL) ;
+ sc.ForwardSetState(SCE_METAPOST_TEXT) ;
+ inString = false ;
+ } else if (sc.atLineEnd) {
+ sc.SetState(SCE_METAPOST_TEXT) ;
+ inTeX = false ;
+ inComment = false ;
+ inClause = false ;
+ inString = false ; // not correct but we want to stimulate one-lines
+ }
+ } else {
+ if ((! isMETAPOSTidentifier(sc.ch)) && (sc.LengthCurrent() > 0)) {
+ if (sc.state == SCE_METAPOST_COMMAND) {
+ sc.GetCurrent(key, sizeof(key)) ;
+ if ((strcmp(key,"btex") == 0) || (strcmp(key,"verbatimtex") == 0)) {
+ sc.ChangeState(SCE_METAPOST_GROUP) ;
+ inTeX = true ;
+ } else if (inTeX) {
+ if (strcmp(key,"etex") == 0) {
+ sc.ChangeState(SCE_METAPOST_GROUP) ;
+ inTeX = false ;
+ } else {
+ sc.ChangeState(SCE_METAPOST_TEXT) ;
+ }
+ } else {
+ if (keywords && keywords.InList(key)) {
+ sc.ChangeState(SCE_METAPOST_COMMAND) ;
+ } else if (keywords2 && keywords2.InList(key)) {
+ sc.ChangeState(SCE_METAPOST_EXTRA) ;
+ } else {
+ sc.ChangeState(SCE_METAPOST_TEXT) ;
+ }
+ }
+ }
+ }
+ if (isMETAPOSTcomment(sc.ch)) {
+ if (! inTeX) {
+ sc.SetState(SCE_METAPOST_SYMBOL) ;
+ sc.ForwardSetState(SCE_METAPOST_DEFAULT) ;
+ inComment = ! processComment ;
+ } else {
+ sc.SetState(SCE_METAPOST_TEXT) ;
+ }
+ } else if (isMETAPOSTstring(sc.ch)) {
+ if (! inTeX) {
+ sc.SetState(SCE_METAPOST_SPECIAL) ;
+ if (! isMETAPOSTstring(sc.chNext)) {
+ sc.ForwardSetState(SCE_METAPOST_TEXT) ;
+ }
+ inString = true ;
+ } else {
+ sc.SetState(SCE_METAPOST_TEXT) ;
+ }
+ } else if (isMETAPOSTcolon(sc.ch)) {
+ if (! inTeX) {
+ if (! isMETAPOSTequal(sc.chNext)) {
+ sc.SetState(SCE_METAPOST_COMMAND) ;
+ inClause = true ;
+ } else {
+ sc.SetState(SCE_METAPOST_SPECIAL) ;
+ }
+ } else {
+ sc.SetState(SCE_METAPOST_TEXT) ;
+ }
+ } else if (isMETAPOSTone(sc.ch)) {
+ if (! inTeX) {
+ sc.SetState(SCE_METAPOST_SPECIAL) ;
+ } else {
+ sc.SetState(SCE_METAPOST_TEXT) ;
+ }
+ } else if (isMETAPOSTtwo(sc.ch)) {
+ if (! inTeX) {
+ sc.SetState(SCE_METAPOST_GROUP) ;
+ } else {
+ sc.SetState(SCE_METAPOST_TEXT) ;
+ }
+ } else if (isMETAPOSTthree(sc.ch)) {
+ if (! inTeX) {
+ sc.SetState(SCE_METAPOST_SYMBOL) ;
+ } else {
+ sc.SetState(SCE_METAPOST_TEXT) ;
+ }
+ } else if (isMETAPOSTidentifier(sc.ch)) {
+ if (sc.state != SCE_METAPOST_COMMAND) {
+ sc.SetState(SCE_METAPOST_TEXT) ;
+ sc.ChangeState(SCE_METAPOST_COMMAND) ;
+ }
+ } else if (isMETAPOSTnumber(sc.ch)) {
+ // rather redundant since for the moment we don't handle numbers
+ sc.SetState(SCE_METAPOST_TEXT) ;
+ } else if (sc.atLineEnd) {
+ sc.SetState(SCE_METAPOST_TEXT) ;
+ inTeX = false ;
+ inComment = false ;
+ inClause = false ;
+ inString = false ;
+ } else {
+ sc.SetState(SCE_METAPOST_TEXT) ;
+ }
+ }
+
+ }
+
+ sc.Complete();
+
+}
+
+// Hooks info the system:
+
+static const char * const metapostWordListDesc[] = {
+ "MetaPost",
+ "MetaFun",
+ 0
+} ;
+
+LexerModule lmMETAPOST(SCLEX_METAPOST, ColouriseMETAPOSTDoc, "metapost", 0, metapostWordListDesc);
diff --git a/src/LexNsis.cpp b/src/LexNsis.cpp
new file mode 100755
index 0000000..6a7274e
--- /dev/null
+++ b/src/LexNsis.cpp
@@ -0,0 +1,647 @@
+// Scintilla source code edit control
+/** @file LexNsis.cxx
+ ** Lexer for NSIS
+ **/
+// Copyright 2003 - 2005 by Angelo Mandato <angelo [at] spaceblue [dot] com>
+// Last Updated: 03/13/2005
+// The License.txt file describes the conditions under which this software may be distributed.
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+/*
+// located in SciLexer.h
+#define SCLEX_NSIS 43
+
+#define SCE_NSIS_DEFAULT 0
+#define SCE_NSIS_COMMENT 1
+#define SCE_NSIS_STRINGDQ 2
+#define SCE_NSIS_STRINGLQ 3
+#define SCE_NSIS_STRINGRQ 4
+#define SCE_NSIS_FUNCTION 5
+#define SCE_NSIS_VARIABLE 6
+#define SCE_NSIS_LABEL 7
+#define SCE_NSIS_USERDEFINED 8
+#define SCE_NSIS_SECTIONDEF 9
+#define SCE_NSIS_SUBSECTIONDEF 10
+#define SCE_NSIS_IFDEFINEDEF 11
+#define SCE_NSIS_MACRODEF 12
+#define SCE_NSIS_STRINGVAR 13
+#define SCE_NSIS_NUMBER 14
+// ADDED for Scintilla v1.63
+#define SCE_NSIS_SECTIONGROUP 15
+#define SCE_NSIS_PAGEEX 16
+#define SCE_NSIS_FUNCTIONDEF 17
+#define SCE_NSIS_COMMENTBOX 18
+*/
+
+static bool isNsisNumber(char ch)
+{
+ return (ch >= '0' && ch <= '9');
+}
+
+static bool isNsisChar(char ch)
+{
+ return (ch == '.' ) || (ch == '_' ) || isNsisNumber(ch) || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
+}
+
+static bool isNsisLetter(char ch)
+{
+ return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
+}
+
+static bool NsisNextLineHasElse(unsigned int start, unsigned int end, Accessor &styler)
+{
+ int nNextLine = -1;
+ for( unsigned int i = start; i < end; i++ )
+ {
+ char cNext = styler.SafeGetCharAt( i );
+ if( cNext == '\n' )
+ {
+ nNextLine = i+1;
+ break;
+ }
+ }
+
+ if( nNextLine == -1 ) // We never foudn the next line...
+ return false;
+
+ for( unsigned int firstChar = nNextLine; firstChar < end; firstChar++ )
+ {
+ char cNext = styler.SafeGetCharAt( firstChar );
+ if( cNext == ' ' )
+ continue;
+ if( cNext == '\t' )
+ continue;
+ if( cNext == '!' )
+ {
+ if( styler.Match(firstChar, "!else") )
+ return true;
+ }
+ break;
+ }
+
+ return false;
+}
+
+static int NsisCmp( char *s1, char *s2, bool bIgnoreCase )
+{
+ if( bIgnoreCase )
+ return CompareCaseInsensitive( s1, s2);
+
+ return strcmp( s1, s2 );
+}
+
+static int calculateFoldNsis(unsigned int start, unsigned int end, int foldlevel, Accessor &styler, bool bElse, bool foldUtilityCmd )
+{
+ int style = styler.StyleAt(end);
+
+ // If the word is too long, it is not what we are looking for
+ if( end - start > 20 )
+ return foldlevel;
+
+ if( foldUtilityCmd )
+ {
+ // Check the style at this point, if it is not valid, then return zero
+ if( style != SCE_NSIS_FUNCTIONDEF && style != SCE_NSIS_SECTIONDEF &&
+ style != SCE_NSIS_SUBSECTIONDEF && style != SCE_NSIS_IFDEFINEDEF &&
+ style != SCE_NSIS_MACRODEF && style != SCE_NSIS_SECTIONGROUP &&
+ style != SCE_NSIS_PAGEEX )
+ return foldlevel;
+ }
+ else
+ {
+ if( style != SCE_NSIS_FUNCTIONDEF && style != SCE_NSIS_SECTIONDEF &&
+ style != SCE_NSIS_SUBSECTIONDEF && style != SCE_NSIS_SECTIONGROUP &&
+ style != SCE_NSIS_PAGEEX )
+ return foldlevel;
+ }
+
+ int newFoldlevel = foldlevel;
+ bool bIgnoreCase = false;
+ if( styler.GetPropertyInt("nsis.ignorecase") == 1 )
+ bIgnoreCase = true;
+
+ char s[20]; // The key word we are looking for has atmost 13 characters
+ for (unsigned int i = 0; i < end - start + 1 && i < 19; i++)
+ {
+ s[i] = static_cast<char>( styler[ start + i ] );
+ s[i + 1] = '\0';
+ }
+
+ if( s[0] == '!' )
+ {
+ if( NsisCmp(s, "!ifndef", bIgnoreCase) == 0 || NsisCmp(s, "!ifdef", bIgnoreCase ) == 0 || NsisCmp(s, "!macro", bIgnoreCase ) == 0 )
+ newFoldlevel++;
+ else if( NsisCmp(s, "!endif", bIgnoreCase) == 0 || NsisCmp(s, "!macroend", bIgnoreCase ) == 0 )
+ newFoldlevel--;
+ else if( bElse && NsisCmp(s, "!else", bIgnoreCase) == 0 )
+ newFoldlevel++;
+ }
+ else
+ {
+ if( NsisCmp(s, "Section", bIgnoreCase ) == 0 || NsisCmp(s, "SectionGroup", bIgnoreCase ) == 0 || NsisCmp(s, "Function", bIgnoreCase) == 0 || NsisCmp(s, "SubSection", bIgnoreCase ) == 0 || NsisCmp(s, "PageEx", bIgnoreCase ) == 0 )
+ newFoldlevel++;
+ else if( NsisCmp(s, "SectionGroupEnd", bIgnoreCase ) == 0 || NsisCmp(s, "SubSectionEnd", bIgnoreCase ) == 0 || NsisCmp(s, "FunctionEnd", bIgnoreCase) == 0 || NsisCmp(s, "SectionEnd", bIgnoreCase ) == 0 || NsisCmp(s, "PageExEnd", bIgnoreCase ) == 0 )
+ newFoldlevel--;
+ }
+
+ return newFoldlevel;
+}
+
+static int classifyWordNsis(unsigned int start, unsigned int end, WordList *keywordLists[], Accessor &styler )
+{
+ bool bIgnoreCase = false;
+ if( styler.GetPropertyInt("nsis.ignorecase") == 1 )
+ bIgnoreCase = true;
+
+ bool bUserVars = false;
+ if( styler.GetPropertyInt("nsis.uservars") == 1 )
+ bUserVars = true;
+
+ char s[100];
+
+ WordList &Functions = *keywordLists[0];
+ WordList &Variables = *keywordLists[1];
+ WordList &Lables = *keywordLists[2];
+ WordList &UserDefined = *keywordLists[3];
+
+ for (unsigned int i = 0; i < end - start + 1 && i < 99; i++)
+ {
+ if( bIgnoreCase )
+ s[i] = static_cast<char>( tolower(styler[ start + i ] ) );
+ else
+ s[i] = static_cast<char>( styler[ start + i ] );
+ s[i + 1] = '\0';
+ }
+
+ // Check for special words...
+ if( NsisCmp(s, "!macro", bIgnoreCase ) == 0 || NsisCmp(s, "!macroend", bIgnoreCase) == 0 ) // Covers !micro and !microend
+ return SCE_NSIS_MACRODEF;
+
+ if( NsisCmp(s, "!ifdef", bIgnoreCase ) == 0 || NsisCmp(s, "!ifndef", bIgnoreCase) == 0 || NsisCmp(s, "!endif", bIgnoreCase) == 0 )
+ return SCE_NSIS_IFDEFINEDEF;
+
+ if( NsisCmp(s, "!else", bIgnoreCase ) == 0 ) // || NsisCmp(s, "!ifndef", bIgnoreCase) == 0 || NsisCmp(s, "!endif", bIgnoreCase) == 0 )
+ return SCE_NSIS_IFDEFINEDEF;
+
+ if( NsisCmp(s, "SectionGroup", bIgnoreCase) == 0 || NsisCmp(s, "SectionGroupEnd", bIgnoreCase) == 0 ) // Covers SectionGroup and SectionGroupEnd
+ return SCE_NSIS_SECTIONGROUP;
+
+ if( NsisCmp(s, "Section", bIgnoreCase ) == 0 || NsisCmp(s, "SectionEnd", bIgnoreCase) == 0 ) // Covers Section and SectionEnd
+ return SCE_NSIS_SECTIONDEF;
+
+ if( NsisCmp(s, "SubSection", bIgnoreCase) == 0 || NsisCmp(s, "SubSectionEnd", bIgnoreCase) == 0 ) // Covers SubSection and SubSectionEnd
+ return SCE_NSIS_SUBSECTIONDEF;
+
+ if( NsisCmp(s, "PageEx", bIgnoreCase) == 0 || NsisCmp(s, "PageExEnd", bIgnoreCase) == 0 ) // Covers PageEx and PageExEnd
+ return SCE_NSIS_PAGEEX;
+
+ if( NsisCmp(s, "Function", bIgnoreCase) == 0 || NsisCmp(s, "FunctionEnd", bIgnoreCase) == 0 ) // Covers Function and FunctionEnd
+ return SCE_NSIS_FUNCTIONDEF;
+
+ if ( Functions.InList(s) )
+ return SCE_NSIS_FUNCTION;
+
+ if ( Variables.InList(s) )
+ return SCE_NSIS_VARIABLE;
+
+ if ( Lables.InList(s) )
+ return SCE_NSIS_LABEL;
+
+ if( UserDefined.InList(s) )
+ return SCE_NSIS_USERDEFINED;
+
+ if( strlen(s) > 3 )
+ {
+ if( s[1] == '{' && s[strlen(s)-1] == '}' )
+ return SCE_NSIS_VARIABLE;
+ }
+
+ // See if the variable is a user defined variable
+ if( s[0] == '$' && bUserVars )
+ {
+ bool bHasSimpleNsisChars = true;
+ for (unsigned int j = 1; j < end - start + 1 && j < 99; j++)
+ {
+ if( !isNsisChar( s[j] ) )
+ {
+ bHasSimpleNsisChars = false;
+ break;
+ }
+ }
+
+ if( bHasSimpleNsisChars )
+ return SCE_NSIS_VARIABLE;
+ }
+
+ // To check for numbers
+ if( isNsisNumber( s[0] ) )
+ {
+ bool bHasSimpleNsisNumber = true;
+ for (unsigned int j = 1; j < end - start + 1 && j < 99; j++)
+ {
+ if( !isNsisNumber( s[j] ) )
+ {
+ bHasSimpleNsisNumber = false;
+ break;
+ }
+ }
+
+ if( bHasSimpleNsisNumber )
+ return SCE_NSIS_NUMBER;
+ }
+
+ return SCE_NSIS_DEFAULT;
+}
+
+static void ColouriseNsisDoc(unsigned int startPos, int length, int, WordList *keywordLists[], Accessor &styler)
+{
+ int state = SCE_NSIS_DEFAULT;
+ if( startPos > 0 )
+ state = styler.StyleAt(startPos-1); // Use the style from the previous line, usually default, but could be commentbox
+
+ styler.StartAt( startPos );
+ styler.GetLine( startPos );
+
+ unsigned int nLengthDoc = startPos + length;
+ styler.StartSegment( startPos );
+
+ char cCurrChar;
+ bool bVarInString = false;
+ bool bClassicVarInString = false;
+
+ unsigned int i;
+ for( i = startPos; i < nLengthDoc; i++ )
+ {
+ cCurrChar = styler.SafeGetCharAt( i );
+ char cNextChar = styler.SafeGetCharAt(i+1);
+
+ switch(state)
+ {
+ case SCE_NSIS_DEFAULT:
+ if( cCurrChar == ';' || cCurrChar == '#' ) // we have a comment line
+ {
+ styler.ColourTo(i-1, state );
+ state = SCE_NSIS_COMMENT;
+ break;
+ }
+ if( cCurrChar == '"' )
+ {
+ styler.ColourTo(i-1, state );
+ state = SCE_NSIS_STRINGDQ;
+ bVarInString = false;
+ bClassicVarInString = false;
+ break;
+ }
+ if( cCurrChar == '\'' )
+ {
+ styler.ColourTo(i-1, state );
+ state = SCE_NSIS_STRINGRQ;
+ bVarInString = false;
+ bClassicVarInString = false;
+ break;
+ }
+ if( cCurrChar == '`' )
+ {
+ styler.ColourTo(i-1, state );
+ state = SCE_NSIS_STRINGLQ;
+ bVarInString = false;
+ bClassicVarInString = false;
+ break;
+ }
+
+ // NSIS KeyWord,Function, Variable, UserDefined:
+ if( cCurrChar == '$' || isNsisChar(cCurrChar) || cCurrChar == '!' )
+ {
+ styler.ColourTo(i-1,state);
+ state = SCE_NSIS_FUNCTION;
+
+ // If it is a number, we must check and set style here first...
+ if( isNsisNumber(cCurrChar) && (cNextChar == '\t' || cNextChar == ' ' || cNextChar == '\r' || cNextChar == '\n' ) )
+ styler.ColourTo( i, SCE_NSIS_NUMBER);
+
+ break;
+ }
+
+ if( cCurrChar == '/' && cNextChar == '*' )
+ {
+ styler.ColourTo(i-1,state);
+ state = SCE_NSIS_COMMENTBOX;
+ break;
+ }
+
+ break;
+ case SCE_NSIS_COMMENT:
+ if( cNextChar == '\n' || cNextChar == '\r' )
+ {
+ // Special case:
+ if( cCurrChar == '\\' )
+ {
+ styler.ColourTo(i-2,state);
+ styler.ColourTo(i,SCE_NSIS_DEFAULT);
+ }
+ else
+ {
+ styler.ColourTo(i,state);
+ state = SCE_NSIS_DEFAULT;
+ }
+ }
+ break;
+ case SCE_NSIS_STRINGDQ:
+ case SCE_NSIS_STRINGLQ:
+ case SCE_NSIS_STRINGRQ:
+
+ if( styler.SafeGetCharAt(i-1) == '\\' && styler.SafeGetCharAt(i-2) == '$' )
+ break; // Ignore the next character, even if it is a quote of some sort
+
+ if( cCurrChar == '"' && state == SCE_NSIS_STRINGDQ )
+ {
+ styler.ColourTo(i,state);
+ state = SCE_NSIS_DEFAULT;
+ break;
+ }
+
+ if( cCurrChar == '`' && state == SCE_NSIS_STRINGLQ )
+ {
+ styler.ColourTo(i,state);
+ state = SCE_NSIS_DEFAULT;
+ break;
+ }
+
+ if( cCurrChar == '\'' && state == SCE_NSIS_STRINGRQ )
+ {
+ styler.ColourTo(i,state);
+ state = SCE_NSIS_DEFAULT;
+ break;
+ }
+
+ if( cNextChar == '\r' || cNextChar == '\n' )
+ {
+ int nCurLine = styler.GetLine(i+1);
+ int nBack = i;
+ // We need to check if the previous line has a \ in it...
+ bool bNextLine = false;
+
+ while( nBack > 0 )
+ {
+ if( styler.GetLine(nBack) != nCurLine )
+ break;
+
+ char cTemp = styler.SafeGetCharAt(nBack, 'a'); // Letter 'a' is safe here
+
+ if( cTemp == '\\' )
+ {
+ bNextLine = true;
+ break;
+ }
+ if( cTemp != '\r' && cTemp != '\n' && cTemp != '\t' && cTemp != ' ' )
+ break;
+
+ nBack--;
+ }
+
+ if( bNextLine )
+ {
+ styler.ColourTo(i+1,state);
+ }
+ if( bNextLine == false )
+ {
+ styler.ColourTo(i,state);
+ state = SCE_NSIS_DEFAULT;
+ }
+ }
+ break;
+
+ case SCE_NSIS_FUNCTION:
+
+ // NSIS KeyWord:
+ if( cCurrChar == '$' )
+ state = SCE_NSIS_DEFAULT;
+ else if( cCurrChar == '\\' && (cNextChar == 'n' || cNextChar == 'r' || cNextChar == 't' ) )
+ state = SCE_NSIS_DEFAULT;
+ else if( (isNsisChar(cCurrChar) && !isNsisChar( cNextChar) && cNextChar != '}') || cCurrChar == '}' )
+ {
+ state = classifyWordNsis( styler.GetStartSegment(), i, keywordLists, styler );
+ styler.ColourTo( i, state);
+ state = SCE_NSIS_DEFAULT;
+ }
+ else if( !isNsisChar( cCurrChar ) && cCurrChar != '{' && cCurrChar != '}' )
+ {
+ if( classifyWordNsis( styler.GetStartSegment(), i-1, keywordLists, styler) == SCE_NSIS_NUMBER )
+ styler.ColourTo( i-1, SCE_NSIS_NUMBER );
+
+ state = SCE_NSIS_DEFAULT;
+
+ if( cCurrChar == '"' )
+ {
+ state = SCE_NSIS_STRINGDQ;
+ bVarInString = false;
+ bClassicVarInString = false;
+ }
+ else if( cCurrChar == '`' )
+ {
+ state = SCE_NSIS_STRINGLQ;
+ bVarInString = false;
+ bClassicVarInString = false;
+ }
+ else if( cCurrChar == '\'' )
+ {
+ state = SCE_NSIS_STRINGRQ;
+ bVarInString = false;
+ bClassicVarInString = false;
+ }
+ else if( cCurrChar == '#' || cCurrChar == ';' )
+ {
+ state = SCE_NSIS_COMMENT;
+ }
+ }
+ break;
+ case SCE_NSIS_COMMENTBOX:
+
+ if( styler.SafeGetCharAt(i-1) == '*' && cCurrChar == '/' )
+ {
+ styler.ColourTo(i,state);
+ state = SCE_NSIS_DEFAULT;
+ }
+ break;
+ }
+
+ if( state == SCE_NSIS_COMMENT || state == SCE_NSIS_COMMENTBOX )
+ {
+ styler.ColourTo(i,state);
+ }
+ else if( state == SCE_NSIS_STRINGDQ || state == SCE_NSIS_STRINGLQ || state == SCE_NSIS_STRINGRQ )
+ {
+ bool bIngoreNextDollarSign = false;
+ bool bUserVars = false;
+ if( styler.GetPropertyInt("nsis.uservars") == 1 )
+ bUserVars = true;
+
+ if( bVarInString && cCurrChar == '$' )
+ {
+ bVarInString = false;
+ bIngoreNextDollarSign = true;
+ }
+ else if( bVarInString && cCurrChar == '\\' && (cNextChar == 'n' || cNextChar == 'r' || cNextChar == 't' || cNextChar == '"' || cNextChar == '`' || cNextChar == '\'' ) )
+ {
+ styler.ColourTo( i+1, SCE_NSIS_STRINGVAR);
+ bVarInString = false;
+ bIngoreNextDollarSign = false;
+ }
+
+ // Covers "$INSTDIR and user vars like $MYVAR"
+ else if( bVarInString && !isNsisChar(cNextChar) )
+ {
+ int nWordState = classifyWordNsis( styler.GetStartSegment(), i, keywordLists, styler);
+ if( nWordState == SCE_NSIS_VARIABLE )
+ styler.ColourTo( i, SCE_NSIS_STRINGVAR);
+ else if( bUserVars )
+ styler.ColourTo( i, SCE_NSIS_STRINGVAR);
+ bVarInString = false;
+ }
+ // Covers "${TEST}..."
+ else if( bClassicVarInString && cNextChar == '}' )
+ {
+ styler.ColourTo( i+1, SCE_NSIS_STRINGVAR);
+ bClassicVarInString = false;
+ }
+
+ // Start of var in string
+ if( !bIngoreNextDollarSign && cCurrChar == '$' && cNextChar == '{' )
+ {
+ styler.ColourTo( i-1, state);
+ bClassicVarInString = true;
+ bVarInString = false;
+ }
+ else if( !bIngoreNextDollarSign && cCurrChar == '$' )
+ {
+ styler.ColourTo( i-1, state);
+ bVarInString = true;
+ bClassicVarInString = false;
+ }
+ }
+ }
+
+ // Colourise remaining document
+ styler.ColourTo(nLengthDoc-1,state);
+}
+
+static void FoldNsisDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler)
+{
+ // No folding enabled, no reason to continue...
+ if( styler.GetPropertyInt("fold") == 0 )
+ return;
+
+ bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) == 1;
+ bool foldUtilityCmd = styler.GetPropertyInt("nsis.foldutilcmd", 1) == 1;
+ bool blockComment = false;
+
+ int lineCurrent = styler.GetLine(startPos);
+ unsigned int safeStartPos = styler.LineStart( lineCurrent );
+
+ bool bArg1 = true;
+ int nWordStart = -1;
+
+ int levelCurrent = SC_FOLDLEVELBASE;
+ if (lineCurrent > 0)
+ levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
+ int levelNext = levelCurrent;
+ int style = styler.StyleAt(safeStartPos);
+ if( style == SCE_NSIS_COMMENTBOX )
+ {
+ if( styler.SafeGetCharAt(safeStartPos) == '/' && styler.SafeGetCharAt(safeStartPos+1) == '*' )
+ levelNext++;
+ blockComment = true;
+ }
+
+ for (unsigned int i = safeStartPos; i < startPos + length; i++)
+ {
+ char chCurr = styler.SafeGetCharAt(i);
+ style = styler.StyleAt(i);
+ if( blockComment && style != SCE_NSIS_COMMENTBOX )
+ {
+ levelNext--;
+ blockComment = false;
+ }
+ else if( !blockComment && style == SCE_NSIS_COMMENTBOX )
+ {
+ levelNext++;
+ blockComment = true;
+ }
+
+ if( bArg1 && !blockComment)
+ {
+ if( nWordStart == -1 && (isNsisLetter(chCurr) || chCurr == '!') )
+ {
+ nWordStart = i;
+ }
+ else if( isNsisLetter(chCurr) == false && nWordStart > -1 )
+ {
+ int newLevel = calculateFoldNsis( nWordStart, i-1, levelNext, styler, foldAtElse, foldUtilityCmd );
+
+ if( newLevel == levelNext )
+ {
+ if( foldAtElse && foldUtilityCmd )
+ {
+ if( NsisNextLineHasElse(i, startPos + length, styler) )
+ levelNext--;
+ }
+ }
+ else
+ levelNext = newLevel;
+ bArg1 = false;
+ }
+ }
+
+ if( chCurr == '\n' )
+ {
+ if( bArg1 && foldAtElse && foldUtilityCmd && !blockComment )
+ {
+ if( NsisNextLineHasElse(i, startPos + length, styler) )
+ levelNext--;
+ }
+
+ // If we are on a new line...
+ int levelUse = levelCurrent;
+ int lev = levelUse | levelNext << 16;
+ if (levelUse < levelNext )
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ if (lev != styler.LevelAt(lineCurrent))
+ styler.SetLevel(lineCurrent, lev);
+
+ lineCurrent++;
+ levelCurrent = levelNext;
+ bArg1 = true; // New line, lets look at first argument again
+ nWordStart = -1;
+ }
+ }
+
+ int levelUse = levelCurrent;
+ int lev = levelUse | levelNext << 16;
+ if (levelUse < levelNext)
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ if (lev != styler.LevelAt(lineCurrent))
+ styler.SetLevel(lineCurrent, lev);
+}
+
+static const char * const nsisWordLists[] = {
+ "Functions",
+ "Variables",
+ "Lables",
+ "UserDefined",
+ 0, };
+
+
+LexerModule lmNsis(SCLEX_NSIS, ColouriseNsisDoc, "nsis", FoldNsisDoc, nsisWordLists);
+
diff --git a/src/LexOpal.cpp b/src/LexOpal.cpp
new file mode 100644
index 0000000..d1d1889
--- /dev/null
+++ b/src/LexOpal.cpp
@@ -0,0 +1,518 @@
+// Scintilla source code edit control
+/** @file LexOpal.cxx
+ ** Lexer for OPAL (functional language similar to Haskell)
+ ** Written by Sebastian Pipping <webmaster@hartwork.org>
+ **/
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+#include "StyleContext.h"
+
+inline static void getRange( unsigned int start, unsigned int end, Accessor & styler, char * s, unsigned int len )
+{
+ unsigned int i = 0;
+ while( ( i < end - start + 1 ) && ( i < len - 1 ) )
+ {
+ s[i] = static_cast<char>( styler[ start + i ] );
+ i++;
+ }
+ s[ i ] = '\0';
+}
+
+inline bool HandleString( unsigned int & cur, unsigned int one_too_much, Accessor & styler )
+{
+ char ch;
+
+ // Wait for string to close
+ bool even_backslash_count = true; // Without gaps in between
+ cur++; // Skip initial quote
+ for( ; ; )
+ {
+ if( cur >= one_too_much )
+ {
+ styler.ColourTo( cur - 1, SCE_OPAL_STRING );
+ return false; // STOP
+ }
+
+ ch = styler.SafeGetCharAt( cur );
+ if( ( ch == '\015' ) || ( ch == '\012' ) ) // Deny multi-line strings
+ {
+ styler.ColourTo( cur - 1, SCE_OPAL_STRING );
+ styler.StartSegment( cur );
+ return true;
+ }
+ else
+ {
+ if( even_backslash_count )
+ {
+ if( ch == '"' )
+ {
+ styler.ColourTo( cur, SCE_OPAL_STRING );
+ cur++;
+ if( cur >= one_too_much )
+ {
+ return false; // STOP
+ }
+ else
+ {
+ styler.StartSegment( cur );
+ return true;
+ }
+ }
+ else if( ch == '\\' )
+ {
+ even_backslash_count = false;
+ }
+ }
+ else
+ {
+ even_backslash_count = true;
+ }
+ }
+
+ cur++;
+ }
+}
+
+inline bool HandleCommentBlock( unsigned int & cur, unsigned int one_too_much, Accessor & styler, bool could_fail )
+{
+ char ch;
+
+ if( could_fail )
+ {
+ cur++;
+ if( cur >= one_too_much )
+ {
+ styler.ColourTo( cur - 1, SCE_OPAL_DEFAULT );
+ return false; // STOP
+ }
+
+ ch = styler.SafeGetCharAt( cur );
+ if( ch != '*' )
+ {
+ styler.ColourTo( cur - 1, SCE_OPAL_DEFAULT );
+ styler.StartSegment( cur );
+ return true;
+ }
+ }
+
+ // Wait for comment close
+ cur++;
+ bool star_found = false;
+ for( ; ; )
+ {
+ if( cur >= one_too_much )
+ {
+ styler.ColourTo( cur - 1, SCE_OPAL_COMMENT_BLOCK );
+ return false; // STOP
+ }
+
+ ch = styler.SafeGetCharAt( cur );
+ if( star_found )
+ {
+ if( ch == '/' )
+ {
+ styler.ColourTo( cur, SCE_OPAL_COMMENT_BLOCK );
+ cur++;
+ if( cur >= one_too_much )
+ {
+ return false; // STOP
+ }
+ else
+ {
+ styler.StartSegment( cur );
+ return true;
+ }
+ }
+ else if( ch != '*' )
+ {
+ star_found = false;
+ }
+ }
+ else if( ch == '*' )
+ {
+ star_found = true;
+ }
+ cur++;
+ }
+}
+
+inline bool HandleCommentLine( unsigned int & cur, unsigned int one_too_much, Accessor & styler, bool could_fail )
+{
+ char ch;
+
+ if( could_fail )
+ {
+ cur++;
+ if( cur >= one_too_much )
+ {
+ styler.ColourTo( cur - 1, SCE_OPAL_DEFAULT );
+ return false; // STOP
+ }
+
+ ch = styler.SafeGetCharAt( cur );
+ if( ch != '-' )
+ {
+ styler.ColourTo( cur - 1, SCE_OPAL_DEFAULT );
+ styler.StartSegment( cur );
+ return true;
+ }
+
+ cur++;
+ if( cur >= one_too_much )
+ {
+ styler.ColourTo( cur - 1, SCE_OPAL_DEFAULT );
+ return false; // STOP
+ }
+
+ ch = styler.SafeGetCharAt( cur );
+ if( ( ch != ' ' ) && ( ch != '\t' ) )
+ {
+ styler.ColourTo( cur - 1, SCE_OPAL_DEFAULT );
+ styler.StartSegment( cur );
+ return true;
+ }
+ }
+
+ // Wait for end of line
+ bool fifteen_found = false;
+
+ for( ; ; )
+ {
+ cur++;
+
+ if( cur >= one_too_much )
+ {
+ styler.ColourTo( cur - 1, SCE_OPAL_COMMENT_LINE );
+ return false; // STOP
+ }
+
+ ch = styler.SafeGetCharAt( cur );
+ if( fifteen_found )
+ {
+/*
+ if( ch == '\012' )
+ {
+ // One newline on Windows (015, 012)
+ }
+ else
+ {
+ // One newline on MAC (015) and another char
+ }
+*/
+ cur--;
+ styler.ColourTo( cur - 1, SCE_OPAL_COMMENT_LINE );
+ styler.StartSegment( cur );
+ return true;
+ }
+ else
+ {
+ if( ch == '\015' )
+ {
+ fifteen_found = true;
+ }
+ else if( ch == '\012' )
+ {
+ // One newline on Linux (012)
+ styler.ColourTo( cur - 1, SCE_OPAL_COMMENT_LINE );
+ styler.StartSegment( cur );
+ return true;
+ }
+ }
+ }
+}
+
+inline bool HandlePar( unsigned int & cur, Accessor & styler )
+{
+ styler.ColourTo( cur, SCE_OPAL_PAR );
+
+ cur++;
+
+ styler.StartSegment( cur );
+ return true;
+}
+
+inline bool HandleSpace( unsigned int & cur, unsigned int one_too_much, Accessor & styler )
+{
+ char ch;
+
+ cur++;
+ for( ; ; )
+ {
+ if( cur >= one_too_much )
+ {
+ styler.ColourTo( cur - 1, SCE_OPAL_SPACE );
+ return false;
+ }
+
+ ch = styler.SafeGetCharAt( cur );
+ switch( ch )
+ {
+ case ' ':
+ case '\t':
+ case '\015':
+ case '\012':
+ cur++;
+ break;
+
+ default:
+ styler.ColourTo( cur - 1, SCE_OPAL_SPACE );
+ styler.StartSegment( cur );
+ return true;
+ }
+ }
+}
+
+inline bool HandleInteger( unsigned int & cur, unsigned int one_too_much, Accessor & styler )
+{
+ char ch;
+
+ for( ; ; )
+ {
+ cur++;
+ if( cur >= one_too_much )
+ {
+ styler.ColourTo( cur - 1, SCE_OPAL_INTEGER );
+ return false; // STOP
+ }
+
+ ch = styler.SafeGetCharAt( cur );
+ if( !isdigit( ch ) )
+ {
+ styler.ColourTo( cur - 1, SCE_OPAL_INTEGER );
+ styler.StartSegment( cur );
+ return true;
+ }
+ }
+}
+
+inline bool HandleWord( unsigned int & cur, unsigned int one_too_much, Accessor & styler, WordList * keywordlists[] )
+{
+ char ch;
+ const unsigned int beg = cur;
+
+ cur++;
+ for( ; ; )
+ {
+ ch = styler.SafeGetCharAt( cur );
+ if( ( ch != '_' ) && ( ch != '-' ) &&
+ !islower( ch ) && !isupper( ch ) && !isdigit( ch ) ) break;
+
+ cur++;
+ if( cur >= one_too_much )
+ {
+ break;
+ }
+ }
+
+ const int ide_len = cur - beg + 1;
+ char * ide = new char[ ide_len ];
+ getRange( beg, cur, styler, ide, ide_len );
+
+ WordList & keywords = *keywordlists[ 0 ];
+ WordList & classwords = *keywordlists[ 1 ];
+
+ if( keywords.InList( ide ) ) // Keyword
+ {
+ delete [] ide;
+
+ styler.ColourTo( cur - 1, SCE_OPAL_KEYWORD );
+ if( cur >= one_too_much )
+ {
+ return false; // STOP
+ }
+ else
+ {
+ styler.StartSegment( cur );
+ return true;
+ }
+ }
+ else if( classwords.InList( ide ) ) // Sort
+ {
+ delete [] ide;
+
+ styler.ColourTo( cur - 1, SCE_OPAL_SORT );
+ if( cur >= one_too_much )
+ {
+ return false; // STOP
+ }
+ else
+ {
+ styler.StartSegment( cur );
+ return true;
+ }
+ }
+ else if( !strcmp( ide, "true" ) || !strcmp( ide, "false" ) ) // Bool const
+ {
+ delete [] ide;
+
+ styler.ColourTo( cur - 1, SCE_OPAL_BOOL_CONST );
+ if( cur >= one_too_much )
+ {
+ return false; // STOP
+ }
+ else
+ {
+ styler.StartSegment( cur );
+ return true;
+ }
+ }
+ else // Unknown keyword
+ {
+ delete [] ide;
+
+ styler.ColourTo( cur - 1, SCE_OPAL_DEFAULT );
+ if( cur >= one_too_much )
+ {
+ return false; // STOP
+ }
+ else
+ {
+ styler.StartSegment( cur );
+ return true;
+ }
+ }
+
+}
+
+inline bool HandleSkip( unsigned int & cur, unsigned int one_too_much, Accessor & styler )
+{
+ cur++;
+ styler.ColourTo( cur - 1, SCE_OPAL_DEFAULT );
+ if( cur >= one_too_much )
+ {
+ return false; // STOP
+ }
+ else
+ {
+ styler.StartSegment( cur );
+ return true;
+ }
+}
+
+static void ColouriseOpalDoc( unsigned int startPos, int length, int initStyle, WordList *keywordlists[], Accessor & styler )
+{
+ styler.StartAt( startPos );
+ styler.StartSegment( startPos );
+
+ unsigned int & cur = startPos;
+ const unsigned int one_too_much = startPos + length;
+
+ int state = initStyle;
+
+ for( ; ; )
+ {
+ switch( state )
+ {
+ case SCE_OPAL_KEYWORD:
+ case SCE_OPAL_SORT:
+ if( !HandleWord( cur, one_too_much, styler, keywordlists ) ) return;
+ state = SCE_OPAL_DEFAULT;
+ break;
+
+ case SCE_OPAL_INTEGER:
+ if( !HandleInteger( cur, one_too_much, styler ) ) return;
+ state = SCE_OPAL_DEFAULT;
+ break;
+
+ case SCE_OPAL_COMMENT_BLOCK:
+ if( !HandleCommentBlock( cur, one_too_much, styler, false ) ) return;
+ state = SCE_OPAL_DEFAULT;
+ break;
+
+ case SCE_OPAL_COMMENT_LINE:
+ if( !HandleCommentLine( cur, one_too_much, styler, false ) ) return;
+ state = SCE_OPAL_DEFAULT;
+ break;
+
+ case SCE_OPAL_STRING:
+ if( !HandleString( cur, one_too_much, styler ) ) return;
+ state = SCE_OPAL_DEFAULT;
+ break;
+
+ default: // SCE_OPAL_DEFAULT:
+ {
+ char ch = styler.SafeGetCharAt( cur );
+
+ switch( ch )
+ {
+ // String
+ case '"':
+ if( !HandleString( cur, one_too_much, styler ) ) return;
+ break;
+
+ // Comment block
+ case '/':
+ if( !HandleCommentBlock( cur, one_too_much, styler, true ) ) return;
+ break;
+
+ // Comment line
+ case '-':
+ if( !HandleCommentLine( cur, one_too_much, styler, true ) ) return;
+ break;
+
+ // Par
+ case '(':
+ case ')':
+ case '[':
+ case ']':
+ case '{':
+ case '}':
+ if( !HandlePar( cur, styler ) ) return;
+ break;
+
+ // Whitespace
+ case ' ':
+ case '\t':
+ case '\015':
+ case '\012':
+ if( !HandleSpace( cur, one_too_much, styler ) ) return;
+ break;
+
+ default:
+ {
+ // Integer
+ if( isdigit( ch ) )
+ {
+ if( !HandleInteger( cur, one_too_much, styler ) ) return;
+ }
+
+ // Keyword
+ else if( islower( ch ) || isupper( ch ) )
+ {
+ if( !HandleWord( cur, one_too_much, styler, keywordlists ) ) return;
+
+ }
+
+ // Skip
+ else
+ {
+ if( !HandleSkip( cur, one_too_much, styler ) ) return;
+ }
+ }
+ }
+
+ break;
+ }
+ }
+ }
+}
+
+static const char * const opalWordListDesc[] = {
+ "Keywords",
+ "Sorts",
+ 0
+};
+
+LexerModule lmOpal(SCLEX_OPAL, ColouriseOpalDoc, "opal", NULL, opalWordListDesc);
diff --git a/src/LexOthers.cpp b/src/LexOthers.cpp
new file mode 100755
index 0000000..5f6e7e4
--- /dev/null
+++ b/src/LexOthers.cpp
@@ -0,0 +1,1140 @@
+// Scintilla source code edit control
+/** @file LexOthers.cxx
+ ** Lexers for batch files, diff results, properties files, make files and error lists.
+ ** Also lexer for LaTeX documents.
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+static bool Is0To9(char ch) {
+ return (ch >= '0') && (ch <= '9');
+}
+
+static bool Is1To9(char ch) {
+ return (ch >= '1') && (ch <= '9');
+}
+
+static inline bool AtEOL(Accessor &styler, unsigned int i) {
+ return (styler[i] == '\n') ||
+ ((styler[i] == '\r') && (styler.SafeGetCharAt(i + 1) != '\n'));
+}
+
+// Tests for BATCH Operators
+static bool IsBOperator(char ch) {
+ return (ch == '=') || (ch == '+') || (ch == '>') || (ch == '<') ||
+ (ch == '|') || (ch == '?') || (ch == '*');
+}
+
+// Tests for BATCH Separators
+static bool IsBSeparator(char ch) {
+ return (ch == '\\') || (ch == '.') || (ch == ';') ||
+ (ch == '\"') || (ch == '\'') || (ch == '/') || (ch == ')');
+}
+
+static void ColouriseBatchLine(
+ char *lineBuffer,
+ unsigned int lengthLine,
+ unsigned int startLine,
+ unsigned int endPos,
+ WordList *keywordlists[],
+ Accessor &styler) {
+
+ unsigned int offset = 0; // Line Buffer Offset
+ unsigned int enVarEnd; // Environment Variable End point
+ unsigned int cmdLoc; // External Command / Program Location
+ char wordBuffer[81]; // Word Buffer - large to catch long paths
+ unsigned int wbl; // Word Buffer Length
+ unsigned int wbo; // Word Buffer Offset - also Special Keyword Buffer Length
+ WordList &keywords = *keywordlists[0]; // Internal Commands
+ WordList &keywords2 = *keywordlists[1]; // External Commands (optional)
+
+ // CHOICE, ECHO, GOTO, PROMPT and SET have Default Text that may contain Regular Keywords
+ // Toggling Regular Keyword Checking off improves readability
+ // Other Regular Keywords and External Commands / Programs might also benefit from toggling
+ // Need a more robust algorithm to properly toggle Regular Keyword Checking
+ bool continueProcessing = true; // Used to toggle Regular Keyword Checking
+ // Special Keywords are those that allow certain characters without whitespace after the command
+ // Examples are: cd. cd\ md. rd. dir| dir> echo: echo. path=
+ // Special Keyword Buffer used to determine if the first n characters is a Keyword
+ char sKeywordBuffer[10]; // Special Keyword Buffer
+ bool sKeywordFound; // Exit Special Keyword for-loop if found
+
+ // Skip initial spaces
+ while ((offset < lengthLine) && (isspacechar(lineBuffer[offset]))) {
+ offset++;
+ }
+ // Colorize Default Text
+ styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT);
+ // Set External Command / Program Location
+ cmdLoc = offset;
+
+ // Check for Fake Label (Comment) or Real Label - return if found
+ if (lineBuffer[offset] == ':') {
+ if (lineBuffer[offset + 1] == ':') {
+ // Colorize Fake Label (Comment) - :: is similar to REM, see http://content.techweb.com/winmag/columns/explorer/2000/21.htm
+ styler.ColourTo(endPos, SCE_BAT_COMMENT);
+ } else {
+ // Colorize Real Label
+ styler.ColourTo(endPos, SCE_BAT_LABEL);
+ }
+ return;
+ // Check for Drive Change (Drive Change is internal command) - return if found
+ } else if ((isalpha(lineBuffer[offset])) &&
+ (lineBuffer[offset + 1] == ':') &&
+ ((isspacechar(lineBuffer[offset + 2])) ||
+ (((lineBuffer[offset + 2] == '\\')) &&
+ (isspacechar(lineBuffer[offset + 3]))))) {
+ // Colorize Regular Keyword
+ styler.ColourTo(endPos, SCE_BAT_WORD);
+ return;
+ }
+
+ // Check for Hide Command (@ECHO OFF/ON)
+ if (lineBuffer[offset] == '@') {
+ styler.ColourTo(startLine + offset, SCE_BAT_HIDE);
+ offset++;
+ // Check for Argument (%n) or Environment Variable (%x...%)
+ } else if (lineBuffer[offset] == '%') {
+ enVarEnd = offset + 1;
+ // Search end of word for second % (can be a long path)
+ while ((enVarEnd < lengthLine) &&
+ (!isspacechar(lineBuffer[enVarEnd])) &&
+ (lineBuffer[enVarEnd] != '%') &&
+ (!IsBOperator(lineBuffer[enVarEnd])) &&
+ (!IsBSeparator(lineBuffer[enVarEnd]))) {
+ enVarEnd++;
+ }
+ // Check for Argument (%n)
+ if ((Is0To9(lineBuffer[offset + 1])) &&
+ (lineBuffer[enVarEnd] != '%')) {
+ // Colorize Argument
+ styler.ColourTo(startLine + offset + 1, SCE_BAT_IDENTIFIER);
+ offset += 2;
+ // Check for External Command / Program
+ if (!isspacechar(lineBuffer[offset])) {
+ cmdLoc = offset;
+ }
+ // Check for Environment Variable (%x...%)
+ } else if ((lineBuffer[offset + 1] != '%') &&
+ (lineBuffer[enVarEnd] == '%')) {
+ offset = enVarEnd;
+ // Colorize Environment Variable
+ styler.ColourTo(startLine + offset, SCE_BAT_IDENTIFIER);
+ offset++;
+ // Check for External Command / Program
+ if (!isspacechar(lineBuffer[offset])) {
+ cmdLoc = offset;
+ }
+ }
+ }
+ // Skip next spaces
+ while ((offset < lengthLine) && (isspacechar(lineBuffer[offset]))) {
+ offset++;
+ }
+
+ // Read remainder of line word-at-a-time or remainder-of-word-at-a-time
+ while (offset < lengthLine) {
+ if (offset > startLine) {
+ // Colorize Default Text
+ styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT);
+ }
+ // Copy word from Line Buffer into Word Buffer
+ wbl = 0;
+ for (; offset < lengthLine && wbl < 80 &&
+ !isspacechar(lineBuffer[offset]); wbl++, offset++) {
+ wordBuffer[wbl] = static_cast<char>(tolower(lineBuffer[offset]));
+ }
+ wordBuffer[wbl] = '\0';
+ wbo = 0;
+
+ // Check for Comment - return if found
+ if (CompareCaseInsensitive(wordBuffer, "rem") == 0) {
+ styler.ColourTo(endPos, SCE_BAT_COMMENT);
+ return;
+ }
+ // Check for Separator
+ if (IsBSeparator(wordBuffer[0])) {
+ // Check for External Command / Program
+ if ((cmdLoc == offset - wbl) &&
+ ((wordBuffer[0] == ':') ||
+ (wordBuffer[0] == '\\') ||
+ (wordBuffer[0] == '.'))) {
+ // Reset Offset to re-process remainder of word
+ offset -= (wbl - 1);
+ // Colorize External Command / Program
+ if (!keywords2) {
+ styler.ColourTo(startLine + offset - 1, SCE_BAT_COMMAND);
+ } else if (keywords2.InList(wordBuffer)) {
+ styler.ColourTo(startLine + offset - 1, SCE_BAT_COMMAND);
+ } else {
+ styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT);
+ }
+ // Reset External Command / Program Location
+ cmdLoc = offset;
+ } else {
+ // Reset Offset to re-process remainder of word
+ offset -= (wbl - 1);
+ // Colorize Default Text
+ styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT);
+ }
+ // Check for Regular Keyword in list
+ } else if ((keywords.InList(wordBuffer)) &&
+ (continueProcessing)) {
+ // ECHO, GOTO, PROMPT and SET require no further Regular Keyword Checking
+ if ((CompareCaseInsensitive(wordBuffer, "echo") == 0) ||
+ (CompareCaseInsensitive(wordBuffer, "goto") == 0) ||
+ (CompareCaseInsensitive(wordBuffer, "prompt") == 0) ||
+ (CompareCaseInsensitive(wordBuffer, "set") == 0)) {
+ continueProcessing = false;
+ }
+ // Identify External Command / Program Location for ERRORLEVEL, and EXIST
+ if ((CompareCaseInsensitive(wordBuffer, "errorlevel") == 0) ||
+ (CompareCaseInsensitive(wordBuffer, "exist") == 0)) {
+ // Reset External Command / Program Location
+ cmdLoc = offset;
+ // Skip next spaces
+ while ((cmdLoc < lengthLine) &&
+ (isspacechar(lineBuffer[cmdLoc]))) {
+ cmdLoc++;
+ }
+ // Skip comparison
+ while ((cmdLoc < lengthLine) &&
+ (!isspacechar(lineBuffer[cmdLoc]))) {
+ cmdLoc++;
+ }
+ // Skip next spaces
+ while ((cmdLoc < lengthLine) &&
+ (isspacechar(lineBuffer[cmdLoc]))) {
+ cmdLoc++;
+ }
+ // Identify External Command / Program Location for CALL, DO, LOADHIGH and LH
+ } else if ((CompareCaseInsensitive(wordBuffer, "call") == 0) ||
+ (CompareCaseInsensitive(wordBuffer, "do") == 0) ||
+ (CompareCaseInsensitive(wordBuffer, "loadhigh") == 0) ||
+ (CompareCaseInsensitive(wordBuffer, "lh") == 0)) {
+ // Reset External Command / Program Location
+ cmdLoc = offset;
+ // Skip next spaces
+ while ((cmdLoc < lengthLine) &&
+ (isspacechar(lineBuffer[cmdLoc]))) {
+ cmdLoc++;
+ }
+ }
+ // Colorize Regular keyword
+ styler.ColourTo(startLine + offset - 1, SCE_BAT_WORD);
+ // No need to Reset Offset
+ // Check for Special Keyword in list, External Command / Program, or Default Text
+ } else if ((wordBuffer[0] != '%') &&
+ (!IsBOperator(wordBuffer[0])) &&
+ (continueProcessing)) {
+ // Check for Special Keyword
+ // Affected Commands are in Length range 2-6
+ // Good that ERRORLEVEL, EXIST, CALL, DO, LOADHIGH, and LH are unaffected
+ sKeywordFound = false;
+ for (unsigned int keywordLength = 2; keywordLength < wbl && keywordLength < 7 && !sKeywordFound; keywordLength++) {
+ wbo = 0;
+ // Copy Keyword Length from Word Buffer into Special Keyword Buffer
+ for (; wbo < keywordLength; wbo++) {
+ sKeywordBuffer[wbo] = static_cast<char>(wordBuffer[wbo]);
+ }
+ sKeywordBuffer[wbo] = '\0';
+ // Check for Special Keyword in list
+ if ((keywords.InList(sKeywordBuffer)) &&
+ ((IsBOperator(wordBuffer[wbo])) ||
+ (IsBSeparator(wordBuffer[wbo])))) {
+ sKeywordFound = true;
+ // ECHO requires no further Regular Keyword Checking
+ if (CompareCaseInsensitive(sKeywordBuffer, "echo") == 0) {
+ continueProcessing = false;
+ }
+ // Colorize Special Keyword as Regular Keyword
+ styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_WORD);
+ // Reset Offset to re-process remainder of word
+ offset -= (wbl - wbo);
+ }
+ }
+ // Check for External Command / Program or Default Text
+ if (!sKeywordFound) {
+ wbo = 0;
+ // Check for External Command / Program
+ if (cmdLoc == offset - wbl) {
+ // Read up to %, Operator or Separator
+ while ((wbo < wbl) &&
+ (wordBuffer[wbo] != '%') &&
+ (!IsBOperator(wordBuffer[wbo])) &&
+ (!IsBSeparator(wordBuffer[wbo]))) {
+ wbo++;
+ }
+ // Reset External Command / Program Location
+ cmdLoc = offset - (wbl - wbo);
+ // Reset Offset to re-process remainder of word
+ offset -= (wbl - wbo);
+ // CHOICE requires no further Regular Keyword Checking
+ if (CompareCaseInsensitive(wordBuffer, "choice") == 0) {
+ continueProcessing = false;
+ }
+ // Check for START (and its switches) - What follows is External Command \ Program
+ if (CompareCaseInsensitive(wordBuffer, "start") == 0) {
+ // Reset External Command / Program Location
+ cmdLoc = offset;
+ // Skip next spaces
+ while ((cmdLoc < lengthLine) &&
+ (isspacechar(lineBuffer[cmdLoc]))) {
+ cmdLoc++;
+ }
+ // Reset External Command / Program Location if command switch detected
+ if (lineBuffer[cmdLoc] == '/') {
+ // Skip command switch
+ while ((cmdLoc < lengthLine) &&
+ (!isspacechar(lineBuffer[cmdLoc]))) {
+ cmdLoc++;
+ }
+ // Skip next spaces
+ while ((cmdLoc < lengthLine) &&
+ (isspacechar(lineBuffer[cmdLoc]))) {
+ cmdLoc++;
+ }
+ }
+ }
+ // Colorize External Command / Program
+ if (!keywords2) {
+ styler.ColourTo(startLine + offset - 1, SCE_BAT_COMMAND);
+ } else if (keywords2.InList(wordBuffer)) {
+ styler.ColourTo(startLine + offset - 1, SCE_BAT_COMMAND);
+ } else {
+ styler.ColourTo(startLine + offset - 1, SCE_BAT_DEFAULT);
+ }
+ // No need to Reset Offset
+ // Check for Default Text
+ } else {
+ // Read up to %, Operator or Separator
+ while ((wbo < wbl) &&
+ (wordBuffer[wbo] != '%') &&
+ (!IsBOperator(wordBuffer[wbo])) &&
+ (!IsBSeparator(wordBuffer[wbo]))) {
+ wbo++;
+ }
+ // Colorize Default Text
+ styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_DEFAULT);
+ // Reset Offset to re-process remainder of word
+ offset -= (wbl - wbo);
+ }
+ }
+ // Check for Argument (%n), Environment Variable (%x...%) or Local Variable (%%a)
+ } else if (wordBuffer[0] == '%') {
+ // Colorize Default Text
+ styler.ColourTo(startLine + offset - 1 - wbl, SCE_BAT_DEFAULT);
+ wbo++;
+ // Search to end of word for second % (can be a long path)
+ while ((wbo < wbl) &&
+ (wordBuffer[wbo] != '%') &&
+ (!IsBOperator(wordBuffer[wbo])) &&
+ (!IsBSeparator(wordBuffer[wbo]))) {
+ wbo++;
+ }
+ // Check for Argument (%n)
+ if ((Is0To9(wordBuffer[1])) &&
+ (wordBuffer[wbo] != '%')) {
+ // Check for External Command / Program
+ if (cmdLoc == offset - wbl) {
+ cmdLoc = offset - (wbl - 2);
+ }
+ // Colorize Argument
+ styler.ColourTo(startLine + offset - 1 - (wbl - 2), SCE_BAT_IDENTIFIER);
+ // Reset Offset to re-process remainder of word
+ offset -= (wbl - 2);
+ // Check for Environment Variable (%x...%)
+ } else if ((wordBuffer[1] != '%') &&
+ (wordBuffer[wbo] == '%')) {
+ wbo++;
+ // Check for External Command / Program
+ if (cmdLoc == offset - wbl) {
+ cmdLoc = offset - (wbl - wbo);
+ }
+ // Colorize Environment Variable
+ styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_IDENTIFIER);
+ // Reset Offset to re-process remainder of word
+ offset -= (wbl - wbo);
+ // Check for Local Variable (%%a)
+ } else if (
+ (wordBuffer[1] == '%') &&
+ (wordBuffer[2] != '%') &&
+ (!IsBOperator(wordBuffer[2])) &&
+ (!IsBSeparator(wordBuffer[2]))) {
+ // Check for External Command / Program
+ if (cmdLoc == offset - wbl) {
+ cmdLoc = offset - (wbl - 3);
+ }
+ // Colorize Local Variable
+ styler.ColourTo(startLine + offset - 1 - (wbl - 3), SCE_BAT_IDENTIFIER);
+ // Reset Offset to re-process remainder of word
+ offset -= (wbl - 3);
+ }
+ // Check for Operator
+ } else if (IsBOperator(wordBuffer[0])) {
+ // Colorize Default Text
+ styler.ColourTo(startLine + offset - 1 - wbl, SCE_BAT_DEFAULT);
+ // Check for Comparison Operator
+ if ((wordBuffer[0] == '=') && (wordBuffer[1] == '=')) {
+ // Identify External Command / Program Location for IF
+ cmdLoc = offset;
+ // Skip next spaces
+ while ((cmdLoc < lengthLine) &&
+ (isspacechar(lineBuffer[cmdLoc]))) {
+ cmdLoc++;
+ }
+ // Colorize Comparison Operator
+ styler.ColourTo(startLine + offset - 1 - (wbl - 2), SCE_BAT_OPERATOR);
+ // Reset Offset to re-process remainder of word
+ offset -= (wbl - 2);
+ // Check for Pipe Operator
+ } else if (wordBuffer[0] == '|') {
+ // Reset External Command / Program Location
+ cmdLoc = offset - wbl + 1;
+ // Skip next spaces
+ while ((cmdLoc < lengthLine) &&
+ (isspacechar(lineBuffer[cmdLoc]))) {
+ cmdLoc++;
+ }
+ // Colorize Pipe Operator
+ styler.ColourTo(startLine + offset - 1 - (wbl - 1), SCE_BAT_OPERATOR);
+ // Reset Offset to re-process remainder of word
+ offset -= (wbl - 1);
+ // Check for Other Operator
+ } else {
+ // Check for > Operator
+ if (wordBuffer[0] == '>') {
+ // Turn Keyword and External Command / Program checking back on
+ continueProcessing = true;
+ }
+ // Colorize Other Operator
+ styler.ColourTo(startLine + offset - 1 - (wbl - 1), SCE_BAT_OPERATOR);
+ // Reset Offset to re-process remainder of word
+ offset -= (wbl - 1);
+ }
+ // Check for Default Text
+ } else {
+ // Read up to %, Operator or Separator
+ while ((wbo < wbl) &&
+ (wordBuffer[wbo] != '%') &&
+ (!IsBOperator(wordBuffer[wbo])) &&
+ (!IsBSeparator(wordBuffer[wbo]))) {
+ wbo++;
+ }
+ // Colorize Default Text
+ styler.ColourTo(startLine + offset - 1 - (wbl - wbo), SCE_BAT_DEFAULT);
+ // Reset Offset to re-process remainder of word
+ offset -= (wbl - wbo);
+ }
+ // Skip next spaces - nothing happens if Offset was Reset
+ while ((offset < lengthLine) && (isspacechar(lineBuffer[offset]))) {
+ offset++;
+ }
+ }
+ // Colorize Default Text for remainder of line - currently not lexed
+ styler.ColourTo(endPos, SCE_BAT_DEFAULT);
+}
+
+static void ColouriseBatchDoc(
+ unsigned int startPos,
+ int length,
+ int /*initStyle*/,
+ WordList *keywordlists[],
+ Accessor &styler) {
+
+ char lineBuffer[1024];
+
+ styler.StartAt(startPos);
+ styler.StartSegment(startPos);
+ unsigned int linePos = 0;
+ unsigned int startLine = startPos;
+ for (unsigned int i = startPos; i < startPos + length; i++) {
+ lineBuffer[linePos++] = styler[i];
+ if (AtEOL(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) {
+ // End of line (or of line buffer) met, colourise it
+ lineBuffer[linePos] = '\0';
+ ColouriseBatchLine(lineBuffer, linePos, startLine, i, keywordlists, styler);
+ linePos = 0;
+ startLine = i + 1;
+ }
+ }
+ if (linePos > 0) { // Last line does not have ending characters
+ ColouriseBatchLine(lineBuffer, linePos, startLine, startPos + length - 1,
+ keywordlists, styler);
+ }
+}
+
+static void ColouriseDiffLine(char *lineBuffer, int endLine, Accessor &styler) {
+ // It is needed to remember the current state to recognize starting
+ // comment lines before the first "diff " or "--- ". If a real
+ // difference starts then each line starting with ' ' is a whitespace
+ // otherwise it is considered a comment (Only in..., Binary file...)
+ if (0 == strncmp(lineBuffer, "diff ", 5)) {
+ styler.ColourTo(endLine, SCE_DIFF_COMMAND);
+ } else if (0 == strncmp(lineBuffer, "--- ", 4)) {
+ // In a context diff, --- appears in both the header and the position markers
+ if (atoi(lineBuffer+4) && !strchr(lineBuffer, '/'))
+ styler.ColourTo(endLine, SCE_DIFF_POSITION);
+ else
+ styler.ColourTo(endLine, SCE_DIFF_HEADER);
+ } else if (0 == strncmp(lineBuffer, "+++ ", 4)) {
+ // I don't know of any diff where "+++ " is a position marker, but for
+ // consistency, do the same as with "--- " and "*** ".
+ if (atoi(lineBuffer+4) && !strchr(lineBuffer, '/'))
+ styler.ColourTo(endLine, SCE_DIFF_POSITION);
+ else
+ styler.ColourTo(endLine, SCE_DIFF_HEADER);
+ } else if (0 == strncmp(lineBuffer, "====", 4)) { // For p4's diff
+ styler.ColourTo(endLine, SCE_DIFF_HEADER);
+ } else if (0 == strncmp(lineBuffer, "***", 3)) {
+ // In a context diff, *** appears in both the header and the position markers.
+ // Also ******** is a chunk header, but here it's treated as part of the
+ // position marker since there is no separate style for a chunk header.
+ if (lineBuffer[3] == ' ' && atoi(lineBuffer+4) && !strchr(lineBuffer, '/'))
+ styler.ColourTo(endLine, SCE_DIFF_POSITION);
+ else if (lineBuffer[3] == '*')
+ styler.ColourTo(endLine, SCE_DIFF_POSITION);
+ else
+ styler.ColourTo(endLine, SCE_DIFF_HEADER);
+ } else if (0 == strncmp(lineBuffer, "? ", 2)) { // For difflib
+ styler.ColourTo(endLine, SCE_DIFF_HEADER);
+ } else if (lineBuffer[0] == '@') {
+ styler.ColourTo(endLine, SCE_DIFF_POSITION);
+ } else if (lineBuffer[0] >= '0' && lineBuffer[0] <= '9') {
+ styler.ColourTo(endLine, SCE_DIFF_POSITION);
+ } else if (lineBuffer[0] == '-' || lineBuffer[0] == '<') {
+ styler.ColourTo(endLine, SCE_DIFF_DELETED);
+ } else if (lineBuffer[0] == '+' || lineBuffer[0] == '>') {
+ styler.ColourTo(endLine, SCE_DIFF_ADDED);
+ } else if (lineBuffer[0] != ' ') {
+ styler.ColourTo(endLine, SCE_DIFF_COMMENT);
+ } else {
+ styler.ColourTo(endLine, SCE_DIFF_DEFAULT);
+ }
+}
+
+static void ColouriseDiffDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
+ char lineBuffer[1024];
+ styler.StartAt(startPos);
+ styler.StartSegment(startPos);
+ unsigned int linePos = 0;
+ for (unsigned int i = startPos; i < startPos + length; i++) {
+ lineBuffer[linePos++] = styler[i];
+ if (AtEOL(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) {
+ // End of line (or of line buffer) met, colourise it
+ lineBuffer[linePos] = '\0';
+ ColouriseDiffLine(lineBuffer, i, styler);
+ linePos = 0;
+ }
+ }
+ if (linePos > 0) { // Last line does not have ending characters
+ ColouriseDiffLine(lineBuffer, startPos + length - 1, styler);
+ }
+}
+
+static void FoldDiffDoc(unsigned int startPos, int length, int, WordList*[], Accessor &styler) {
+ int curLine = styler.GetLine(startPos);
+ int prevLevel = SC_FOLDLEVELBASE;
+ if (curLine > 0)
+ prevLevel = styler.LevelAt(curLine-1);
+
+ int curLineStart = styler.LineStart(curLine);
+ do {
+ int nextLevel = prevLevel;
+ if (prevLevel & SC_FOLDLEVELHEADERFLAG)
+ nextLevel = (prevLevel & SC_FOLDLEVELNUMBERMASK) + 1;
+
+ int lineType = styler.StyleAt(curLineStart);
+ if (lineType == SCE_DIFF_COMMAND)
+ nextLevel = (SC_FOLDLEVELBASE + 1) | SC_FOLDLEVELHEADERFLAG;
+ else if (lineType == SCE_DIFF_HEADER) {
+ nextLevel = (SC_FOLDLEVELBASE + 2) | SC_FOLDLEVELHEADERFLAG;
+ } else if (lineType == SCE_DIFF_POSITION)
+ nextLevel = (SC_FOLDLEVELBASE + 3) | SC_FOLDLEVELHEADERFLAG;
+
+ if ((nextLevel & SC_FOLDLEVELHEADERFLAG) && (nextLevel == prevLevel))
+ styler.SetLevel(curLine-1, prevLevel & ~SC_FOLDLEVELHEADERFLAG);
+
+ styler.SetLevel(curLine, nextLevel);
+ prevLevel = nextLevel;
+
+ curLineStart = styler.LineStart(++curLine);
+ } while (static_cast<int>(startPos) + length > curLineStart);
+}
+
+
+static void ColourisePropsLine(
+ char *lineBuffer,
+ unsigned int lengthLine,
+ unsigned int startLine,
+ unsigned int endPos,
+ Accessor &styler) {
+
+ unsigned int i = 0;
+ while ((i < lengthLine) && isspacechar(lineBuffer[i])) // Skip initial spaces
+ i++;
+ if (i < lengthLine) {
+ if (lineBuffer[i] == '#' || lineBuffer[i] == '!' || lineBuffer[i] == ';') {
+ styler.ColourTo(endPos, SCE_PROPS_COMMENT);
+ } else if (lineBuffer[i] == '[') {
+ styler.ColourTo(endPos, SCE_PROPS_SECTION);
+ } else if (lineBuffer[i] == '@') {
+ styler.ColourTo(startLine + i, SCE_PROPS_DEFVAL);
+ if (lineBuffer[++i] == '=')
+ styler.ColourTo(startLine + i, SCE_PROPS_ASSIGNMENT);
+ styler.ColourTo(endPos, SCE_PROPS_DEFAULT);
+ } else {
+ // Search for the '=' character
+ while ((i < lengthLine) && (lineBuffer[i] != '='))
+ i++;
+ if ((i < lengthLine) && (lineBuffer[i] == '=')) {
+ styler.ColourTo(startLine + i - 1, SCE_PROPS_KEY);
+ styler.ColourTo(startLine + i, SCE_PROPS_ASSIGNMENT);
+ styler.ColourTo(endPos, SCE_PROPS_DEFAULT);
+ } else {
+ styler.ColourTo(endPos, SCE_PROPS_DEFAULT);
+ }
+ }
+ } else {
+ styler.ColourTo(endPos, SCE_PROPS_DEFAULT);
+ }
+}
+
+static void ColourisePropsDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
+ char lineBuffer[1024];
+ styler.StartAt(startPos);
+ styler.StartSegment(startPos);
+ unsigned int linePos = 0;
+ unsigned int startLine = startPos;
+ for (unsigned int i = startPos; i < startPos + length; i++) {
+ lineBuffer[linePos++] = styler[i];
+ if (AtEOL(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) {
+ // End of line (or of line buffer) met, colourise it
+ lineBuffer[linePos] = '\0';
+ ColourisePropsLine(lineBuffer, linePos, startLine, i, styler);
+ linePos = 0;
+ startLine = i + 1;
+ }
+ }
+ if (linePos > 0) { // Last line does not have ending characters
+ ColourisePropsLine(lineBuffer, linePos, startLine, startPos + length - 1, styler);
+ }
+}
+
+// adaption by ksc, using the "} else {" trick of 1.53
+// 030721
+static void FoldPropsDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+
+ unsigned int endPos = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+
+ char chNext = styler[startPos];
+ int styleNext = styler.StyleAt(startPos);
+ bool headerPoint = false;
+ int lev;
+
+ for (unsigned int i = startPos; i < endPos; i++) {
+ char ch = chNext;
+ chNext = styler[i+1];
+
+ int style = styleNext;
+ styleNext = styler.StyleAt(i + 1);
+ bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
+
+ if (style == SCE_PROPS_SECTION) {
+ headerPoint = true;
+ }
+
+ if (atEOL) {
+ lev = SC_FOLDLEVELBASE;
+
+ if (lineCurrent > 0) {
+ int levelPrevious = styler.LevelAt(lineCurrent - 1);
+
+ if (levelPrevious & SC_FOLDLEVELHEADERFLAG) {
+ lev = SC_FOLDLEVELBASE + 1;
+ } else {
+ lev = levelPrevious & SC_FOLDLEVELNUMBERMASK;
+ }
+ }
+
+ if (headerPoint) {
+ lev = SC_FOLDLEVELBASE;
+ }
+ if (visibleChars == 0 && foldCompact)
+ lev |= SC_FOLDLEVELWHITEFLAG;
+
+ if (headerPoint) {
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ }
+ if (lev != styler.LevelAt(lineCurrent)) {
+ styler.SetLevel(lineCurrent, lev);
+ }
+
+ lineCurrent++;
+ visibleChars = 0;
+ headerPoint = false;
+ }
+ if (!isspacechar(ch))
+ visibleChars++;
+ }
+
+ if (lineCurrent > 0) {
+ int levelPrevious = styler.LevelAt(lineCurrent - 1);
+ if (levelPrevious & SC_FOLDLEVELHEADERFLAG) {
+ lev = SC_FOLDLEVELBASE + 1;
+ } else {
+ lev = levelPrevious & SC_FOLDLEVELNUMBERMASK;
+ }
+ } else {
+ lev = SC_FOLDLEVELBASE;
+ }
+ int flagsNext = styler.LevelAt(lineCurrent);
+ styler.SetLevel(lineCurrent, lev | flagsNext & ~SC_FOLDLEVELNUMBERMASK);
+}
+
+static void ColouriseMakeLine(
+ char *lineBuffer,
+ unsigned int lengthLine,
+ unsigned int startLine,
+ unsigned int endPos,
+ Accessor &styler) {
+
+ unsigned int i = 0;
+ int lastNonSpace = -1;
+ unsigned int state = SCE_MAKE_DEFAULT;
+ bool bSpecial = false;
+ // Skip initial spaces
+ while ((i < lengthLine) && isspacechar(lineBuffer[i])) {
+ i++;
+ }
+ if (lineBuffer[i] == '#') { // Comment
+ styler.ColourTo(endPos, SCE_MAKE_COMMENT);
+ return;
+ }
+ if (lineBuffer[i] == '!') { // Special directive
+ styler.ColourTo(endPos, SCE_MAKE_PREPROCESSOR);
+ return;
+ }
+ while (i < lengthLine) {
+ if (lineBuffer[i] == '$' && lineBuffer[i + 1] == '(') {
+ styler.ColourTo(startLine + i - 1, state);
+ state = SCE_MAKE_IDENTIFIER;
+ } else if (state == SCE_MAKE_IDENTIFIER && lineBuffer[i] == ')') {
+ styler.ColourTo(startLine + i, state);
+ state = SCE_MAKE_DEFAULT;
+ }
+ if (!bSpecial) {
+ if (lineBuffer[i] == ':') {
+ // We should check that no colouring was made since the beginning of the line,
+ // to avoid colouring stuff like /OUT:file
+ if (lastNonSpace >= 0)
+ styler.ColourTo(startLine + lastNonSpace, SCE_MAKE_TARGET);
+ styler.ColourTo(startLine + i - 1, SCE_MAKE_DEFAULT);
+ styler.ColourTo(startLine + i, SCE_MAKE_OPERATOR);
+ bSpecial = true; // Only react to the first ':' of the line
+ state = SCE_MAKE_DEFAULT;
+ } else if (lineBuffer[i] == '=') {
+ if (lastNonSpace >= 0)
+ styler.ColourTo(startLine + lastNonSpace, SCE_MAKE_IDENTIFIER);
+ styler.ColourTo(startLine + i - 1, SCE_MAKE_DEFAULT);
+ styler.ColourTo(startLine + i, SCE_MAKE_OPERATOR);
+ bSpecial = true; // Only react to the first '=' of the line
+ state = SCE_MAKE_DEFAULT;
+ }
+ }
+ if (!isspacechar(lineBuffer[i])) {
+ lastNonSpace = i;
+ }
+ i++;
+ }
+ if (state == SCE_MAKE_IDENTIFIER) {
+ styler.ColourTo(endPos, SCE_MAKE_IDEOL); // Error, variable reference not ended
+ } else {
+ styler.ColourTo(endPos, SCE_MAKE_DEFAULT);
+ }
+}
+
+static void ColouriseMakeDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
+ char lineBuffer[1024];
+ styler.StartAt(startPos);
+ styler.StartSegment(startPos);
+ unsigned int linePos = 0;
+ unsigned int startLine = startPos;
+ for (unsigned int i = startPos; i < startPos + length; i++) {
+ lineBuffer[linePos++] = styler[i];
+ if (AtEOL(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) {
+ // End of line (or of line buffer) met, colourise it
+ lineBuffer[linePos] = '\0';
+ ColouriseMakeLine(lineBuffer, linePos, startLine, i, styler);
+ linePos = 0;
+ startLine = i + 1;
+ }
+ }
+ if (linePos > 0) { // Last line does not have ending characters
+ ColouriseMakeLine(lineBuffer, linePos, startLine, startPos + length - 1, styler);
+ }
+}
+
+static bool strstart(const char *haystack, const char *needle) {
+ return strncmp(haystack, needle, strlen(needle)) == 0;
+}
+
+static int RecogniseErrorListLine(const char *lineBuffer, unsigned int lengthLine) {
+ if (lineBuffer[0] == '>') {
+ // Command or return status
+ return SCE_ERR_CMD;
+ } else if (lineBuffer[0] == '<') {
+ // Diff removal, but not interested. Trapped to avoid hitting CTAG cases.
+ return SCE_ERR_DEFAULT;
+ } else if (lineBuffer[0] == '!') {
+ return SCE_ERR_DIFF_CHANGED;
+ } else if (lineBuffer[0] == '+') {
+ if (strstart(lineBuffer, "+++ ")) {
+ return SCE_ERR_DIFF_MESSAGE;
+ } else {
+ return SCE_ERR_DIFF_ADDITION;
+ }
+ } else if (lineBuffer[0] == '-') {
+ if (strstart(lineBuffer, "--- ")) {
+ return SCE_ERR_DIFF_MESSAGE;
+ } else {
+ return SCE_ERR_DIFF_DELETION;
+ }
+ } else if (strstart(lineBuffer, "cf90-")) {
+ // Absoft Pro Fortran 90/95 v8.2 error and/or warning message
+ return SCE_ERR_ABSF;
+ } else if (strstart(lineBuffer, "fortcom:")) {
+ // Intel Fortran Compiler v8.0 error/warning message
+ return SCE_ERR_IFORT;
+ } else if (strstr(lineBuffer, "File \"") && strstr(lineBuffer, ", line ")) {
+ return SCE_ERR_PYTHON;
+ } else if (strstr(lineBuffer, " in ") && strstr(lineBuffer, " on line ")) {
+ return SCE_ERR_PHP;
+ } else if ((strstart(lineBuffer, "Error ") ||
+ strstart(lineBuffer, "Warning ")) &&
+ strstr(lineBuffer, " at (") &&
+ strstr(lineBuffer, ") : ") &&
+ (strstr(lineBuffer, " at (") < strstr(lineBuffer, ") : "))) {
+ // Intel Fortran Compiler error/warning message
+ return SCE_ERR_IFC;
+ } else if (strstart(lineBuffer, "Error ")) {
+ // Borland error message
+ return SCE_ERR_BORLAND;
+ } else if (strstart(lineBuffer, "Warning ")) {
+ // Borland warning message
+ return SCE_ERR_BORLAND;
+ } else if (strstr(lineBuffer, "at line " ) &&
+ (strstr(lineBuffer, "at line " ) < (lineBuffer + lengthLine)) &&
+ strstr(lineBuffer, "file ") &&
+ (strstr(lineBuffer, "file ") < (lineBuffer + lengthLine))) {
+ // Lua 4 error message
+ return SCE_ERR_LUA;
+ } else if (strstr(lineBuffer, " at " ) &&
+ (strstr(lineBuffer, " at " ) < (lineBuffer + lengthLine)) &&
+ strstr(lineBuffer, " line ") &&
+ (strstr(lineBuffer, " line ") < (lineBuffer + lengthLine)) &&
+ (strstr(lineBuffer, " at " ) < (strstr(lineBuffer, " line ")))) {
+ // perl error message
+ return SCE_ERR_PERL;
+ } else if ((memcmp(lineBuffer, " at ", 6) == 0) &&
+ strstr(lineBuffer, ":line ")) {
+ // A .NET traceback
+ return SCE_ERR_NET;
+ } else if (strstart(lineBuffer, "Line ") &&
+ strstr(lineBuffer, ", file ")) {
+ // Essential Lahey Fortran error message
+ return SCE_ERR_ELF;
+ } else if (strstart(lineBuffer, "line ") &&
+ strstr(lineBuffer, " column ")) {
+ // HTML tidy style: line 42 column 1
+ return SCE_ERR_TIDY;
+ } else if (strstart(lineBuffer, "\tat ") &&
+ strstr(lineBuffer, "(") &&
+ strstr(lineBuffer, ".java:")) {
+ // Java stack back trace
+ return SCE_ERR_JAVA_STACK;
+ } else {
+ // Look for one of the following formats:
+ // GCC: <filename>:<line>:<message>
+ // Microsoft: <filename>(<line>) :<message>
+ // Common: <filename>(<line>): warning|error|note|remark|catastrophic|fatal
+ // Common: <filename>(<line>) warning|error|note|remark|catastrophic|fatal
+ // Microsoft: <filename>(<line>,<column>)<message>
+ // CTags: \t<message>
+ // Lua 5 traceback: \t<filename>:<line>:<message>
+ bool initialTab = (lineBuffer[0] == '\t');
+ enum { stInitial,
+ stGccStart, stGccDigit, stGcc,
+ stMsStart, stMsDigit, stMsBracket, stMsVc, stMsDigitComma, stMsDotNet,
+ stCtagsStart, stCtagsStartString, stCtagsStringDollar, stCtags,
+ stUnrecognized
+ } state = stInitial;
+ for (unsigned int i = 0; i < lengthLine; i++) {
+ char ch = lineBuffer[i];
+ char chNext = ' ';
+ if ((i + 1) < lengthLine)
+ chNext = lineBuffer[i + 1];
+ if (state == stInitial) {
+ if (ch == ':') {
+ // May be GCC, or might be Lua 5 (Lua traceback same but with tab prefix)
+ if ((chNext != '\\') && (chNext != '/')) {
+ // This check is not completely accurate as may be on
+ // GTK+ with a file name that includes ':'.
+ state = stGccStart;
+ }
+ } else if ((ch == '(') && Is1To9(chNext) && (!initialTab)) {
+ // May be Microsoft
+ // Check against '0' often removes phone numbers
+ state = stMsStart;
+ } else if ((ch == '\t') && (!initialTab)) {
+ // May be CTags
+ state = stCtagsStart;
+ }
+ } else if (state == stGccStart) { // <filename>:
+ state = Is1To9(ch) ? stGccDigit : stUnrecognized;
+ } else if (state == stGccDigit) { // <filename>:<line>
+ if (ch == ':') {
+ state = stGcc; // :9.*: is GCC
+ break;
+ } else if (!Is0To9(ch)) {
+ state = stUnrecognized;
+ }
+ } else if (state == stMsStart) { // <filename>(
+ state = Is0To9(ch) ? stMsDigit : stUnrecognized;
+ } else if (state == stMsDigit) { // <filename>(<line>
+ if (ch == ',') {
+ state = stMsDigitComma;
+ } else if (ch == ')') {
+ state = stMsBracket;
+ } else if ((ch != ' ') && !Is0To9(ch)) {
+ state = stUnrecognized;
+ }
+ } else if (state == stMsBracket) { // <filename>(<line>)
+ if ((ch == ' ') && (chNext == ':')) {
+ state = stMsVc;
+ } else if ((ch == ':' && chNext == ' ') || (ch == ' ')) {
+ // Possibly Delphi.. don't test against chNext as it's one of the strings below.
+ char word[512];
+ unsigned int j, chPos;
+ unsigned numstep;
+ chPos = 0;
+ if (ch == ' ')
+ numstep = 1; // ch was ' ', handle as if it's a delphi errorline, only add 1 to i.
+ else
+ numstep = 2; // otherwise add 2.
+ for (j = i + numstep; j < lengthLine && isalpha(lineBuffer[j]) && chPos < sizeof(word) - 1; j++)
+ word[chPos++] = lineBuffer[j];
+ word[chPos] = 0;
+ if (!CompareCaseInsensitive(word, "error") || !CompareCaseInsensitive(word, "warning") ||
+ !CompareCaseInsensitive(word, "fatal") || !CompareCaseInsensitive(word, "catastrophic") ||
+ !CompareCaseInsensitive(word, "note") || !CompareCaseInsensitive(word, "remark")) {
+ state = stMsVc;
+ } else
+ state = stUnrecognized;
+ } else {
+ state = stUnrecognized;
+ }
+ } else if (state == stMsDigitComma) { // <filename>(<line>,
+ if (ch == ')') {
+ state = stMsDotNet;
+ break;
+ } else if ((ch != ' ') && !Is0To9(ch)) {
+ state = stUnrecognized;
+ }
+ } else if (state == stCtagsStart) {
+ if ((lineBuffer[i - 1] == '\t') &&
+ ((ch == '/' && lineBuffer[i + 1] == '^') || Is0To9(ch))) {
+ state = stCtags;
+ break;
+ } else if ((ch == '/') && (lineBuffer[i + 1] == '^')) {
+ state = stCtagsStartString;
+ }
+ } else if ((state == stCtagsStartString) && ((lineBuffer[i] == '$') && (lineBuffer[i + 1] == '/'))) {
+ state = stCtagsStringDollar;
+ break;
+ }
+ }
+ if (state == stGcc) {
+ return SCE_ERR_GCC;
+ } else if ((state == stMsVc) || (state == stMsDotNet)) {
+ return SCE_ERR_MS;
+ } else if ((state == stCtagsStringDollar) || (state == stCtags)) {
+ return SCE_ERR_CTAG;
+ } else {
+ return SCE_ERR_DEFAULT;
+ }
+ }
+}
+
+static void ColouriseErrorListLine(
+ char *lineBuffer,
+ unsigned int lengthLine,
+ unsigned int endPos,
+ Accessor &styler) {
+ styler.ColourTo(endPos, RecogniseErrorListLine(lineBuffer, lengthLine));
+}
+
+static void ColouriseErrorListDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler) {
+ char lineBuffer[10000];
+ styler.StartAt(startPos);
+ styler.StartSegment(startPos);
+ unsigned int linePos = 0;
+ for (unsigned int i = startPos; i < startPos + length; i++) {
+ lineBuffer[linePos++] = styler[i];
+ if (AtEOL(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) {
+ // End of line (or of line buffer) met, colourise it
+ lineBuffer[linePos] = '\0';
+ ColouriseErrorListLine(lineBuffer, linePos, i, styler);
+ linePos = 0;
+ }
+ }
+ if (linePos > 0) { // Last line does not have ending characters
+ ColouriseErrorListLine(lineBuffer, linePos, startPos + length - 1, styler);
+ }
+}
+
+static int isSpecial(char s) {
+ return (s == '\\') || (s == ',') || (s == ';') || (s == '\'') || (s == ' ') ||
+ (s == '\"') || (s == '`') || (s == '^') || (s == '~');
+}
+
+static int isTag(int start, Accessor &styler) {
+ char s[6];
+ unsigned int i = 0, e = 1;
+ while (i < 5 && e) {
+ s[i] = styler[start + i];
+ i++;
+ e = styler[start + i] != '{';
+ }
+ s[i] = '\0';
+ return (strcmp(s, "begin") == 0) || (strcmp(s, "end") == 0);
+}
+
+static void ColouriseLatexDoc(unsigned int startPos, int length, int initStyle,
+ WordList *[], Accessor &styler) {
+
+ styler.StartAt(startPos);
+
+ int state = initStyle;
+ char chNext = styler[startPos];
+ styler.StartSegment(startPos);
+ int lengthDoc = startPos + length;
+
+ for (int i = startPos; i < lengthDoc; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+
+ if (styler.IsLeadByte(ch)) {
+ chNext = styler.SafeGetCharAt(i + 2);
+ i++;
+ continue;
+ }
+ switch (state) {
+ case SCE_L_DEFAULT :
+ switch (ch) {
+ case '\\' :
+ styler.ColourTo(i - 1, state);
+ if (isSpecial(styler[i + 1])) {
+ styler.ColourTo(i + 1, SCE_L_COMMAND);
+ i++;
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else {
+ if (isTag(i + 1, styler))
+ state = SCE_L_TAG;
+ else
+ state = SCE_L_COMMAND;
+ }
+ break;
+ case '$' :
+ styler.ColourTo(i - 1, state);
+ state = SCE_L_MATH;
+ if (chNext == '$') {
+ i++;
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ break;
+ case '%' :
+ styler.ColourTo(i - 1, state);
+ state = SCE_L_COMMENT;
+ break;
+ }
+ break;
+ case SCE_L_COMMAND :
+ if (chNext == '[' || chNext == '{' || chNext == '}' ||
+ chNext == ' ' || chNext == '\r' || chNext == '\n') {
+ styler.ColourTo(i, state);
+ state = SCE_L_DEFAULT;
+ i++;
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ break;
+ case SCE_L_TAG :
+ if (ch == '}') {
+ styler.ColourTo(i, state);
+ state = SCE_L_DEFAULT;
+ }
+ break;
+ case SCE_L_MATH :
+ if (ch == '$') {
+ if (chNext == '$') {
+ i++;
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ styler.ColourTo(i, state);
+ state = SCE_L_DEFAULT;
+ }
+ break;
+ case SCE_L_COMMENT :
+ if (ch == '\r' || ch == '\n') {
+ styler.ColourTo(i - 1, state);
+ state = SCE_L_DEFAULT;
+ }
+ }
+ }
+ styler.ColourTo(lengthDoc-1, state);
+}
+
+static const char * const batchWordListDesc[] = {
+ "Internal Commands",
+ "External Commands",
+ 0
+};
+
+static const char * const emptyWordListDesc[] = {
+ 0
+};
+
+static void ColouriseNullDoc(unsigned int startPos, int length, int, WordList *[],
+ Accessor &styler) {
+ // Null language means all style bytes are 0 so just mark the end - no need to fill in.
+ if (length > 0) {
+ styler.StartAt(startPos + length - 1);
+ styler.StartSegment(startPos + length - 1);
+ styler.ColourTo(startPos + length - 1, 0);
+ }
+}
+
+LexerModule lmBatch(SCLEX_BATCH, ColouriseBatchDoc, "batch", 0, batchWordListDesc);
+LexerModule lmDiff(SCLEX_DIFF, ColouriseDiffDoc, "diff", FoldDiffDoc, emptyWordListDesc);
+LexerModule lmProps(SCLEX_PROPERTIES, ColourisePropsDoc, "props", FoldPropsDoc, emptyWordListDesc);
+LexerModule lmMake(SCLEX_MAKEFILE, ColouriseMakeDoc, "makefile", 0, emptyWordListDesc);
+LexerModule lmErrorList(SCLEX_ERRORLIST, ColouriseErrorListDoc, "errorlist", 0, emptyWordListDesc);
+LexerModule lmLatex(SCLEX_LATEX, ColouriseLatexDoc, "latex", 0, emptyWordListDesc);
+LexerModule lmNull(SCLEX_NULL, ColouriseNullDoc, "null");
diff --git a/src/LexPB.cpp b/src/LexPB.cpp
new file mode 100755
index 0000000..7878a6b
--- /dev/null
+++ b/src/LexPB.cpp
@@ -0,0 +1,358 @@
+// Scintilla source code edit control
+// @file LexPB.cxx
+// Lexer for PowerBasic by Roland Walter, roland@rowalt.de (for PowerBasic see www.powerbasic.com)
+//
+// Changes:
+// 17.10.2003: Toggling of subs/functions now until next sub/function - this gives better results
+// 29.10.2003: 1. Bug: Toggling didn't work for subs/functions added in editor
+// 2. Own colors for PB constants and Inline Assembler SCE_B_CONSTANT and SCE_B_ASM
+// 3. Several smaller syntax coloring improvements and speed optimizations
+// 12.07.2004: 1. Toggling for macros added
+// 2. Further folding speed optimitations (for people dealing with very large listings)
+//
+// Necessary changes for the PB lexer in Scintilla project:
+// - In SciLexer.h and Scintilla.iface:
+//
+// #define SCLEX_POWERBASIC 51 //ID for PowerBasic lexer
+// (...)
+// #define SCE_B_DEFAULT 0 //in both VB and PB lexer
+// #define SCE_B_COMMENT 1 //in both VB and PB lexer
+// #define SCE_B_NUMBER 2 //in both VB and PB lexer
+// #define SCE_B_KEYWORD 3 //in both VB and PB lexer
+// #define SCE_B_STRING 4 //in both VB and PB lexer
+// #define SCE_B_PREPROCESSOR 5 //VB lexer only, not in PB lexer
+// #define SCE_B_OPERATOR 6 //in both VB and PB lexer
+// #define SCE_B_IDENTIFIER 7 //in both VB and PB lexer
+// #define SCE_B_DATE 8 //VB lexer only, not in PB lexer
+// #define SCE_B_CONSTANT 13 //PB lexer only, not in VB lexer
+// #define SCE_B_ASM 14 //PB lexer only, not in VB lexer
+
+// - Statement added to KeyWords.cxx: 'LINK_LEXER(lmPB);'
+// - Statement added to scintilla_vc6.mak: '$(DIR_O)\LexPB.obj: ...\src\LexPB.cxx $(LEX_HEADERS)'
+//
+// Copyright for Scintilla: 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+static inline bool IsTypeCharacter(const int ch)
+{
+ return ch == '%' || ch == '&' || ch == '@' || ch == '!' || ch == '#' || ch == '$' || ch == '?';
+}
+
+static inline bool IsAWordChar(const int ch)
+{
+ return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_');
+}
+
+static inline bool IsAWordStart(const int ch)
+{
+ return (ch < 0x80) && (isalnum(ch) || ch == '_');
+}
+
+bool MatchUpperCase(Accessor &styler, int pos, const char *s) //Same as styler.Match() but uppercase comparison (a-z,A-Z and space only)
+{
+ char ch;
+ for (int i=0; *s; i++)
+ {
+ ch=styler.SafeGetCharAt(pos+i);
+ if (ch > 0x60) ch -= '\x20';
+ if (*s != ch) return false;
+ s++;
+ }
+ return true;
+}
+
+static void ColourisePBDoc(unsigned int startPos, int length, int initStyle,WordList *keywordlists[],Accessor &styler) {
+
+ WordList &keywords = *keywordlists[0];
+
+ styler.StartAt(startPos);
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ for (; sc.More(); sc.Forward()) {
+ switch (sc.state)
+ {
+ case SCE_B_OPERATOR:
+ {
+ sc.SetState(SCE_B_DEFAULT);
+ break;
+ }
+ case SCE_B_KEYWORD:
+ {
+ if (!IsAWordChar(sc.ch))
+ {
+ if (!IsTypeCharacter(sc.ch))
+ {
+ char s[100];
+ sc.GetCurrentLowered(s, sizeof(s));
+ if (keywords.InList(s))
+ {
+ if (strcmp(s, "rem") == 0)
+ {
+ sc.ChangeState(SCE_B_COMMENT);
+ if (sc.atLineEnd) {sc.SetState(SCE_B_DEFAULT);}
+ }
+ else if (strcmp(s, "asm") == 0)
+ {
+ sc.ChangeState(SCE_B_ASM);
+ if (sc.atLineEnd) {sc.SetState(SCE_B_DEFAULT);}
+ }
+ else
+ {
+ sc.SetState(SCE_B_DEFAULT);
+ }
+ }
+ else
+ {
+ sc.ChangeState(SCE_B_IDENTIFIER);
+ sc.SetState(SCE_B_DEFAULT);
+ }
+ }
+ }
+ break;
+ }
+ case SCE_B_NUMBER:
+ {
+ if (!IsAWordChar(sc.ch)) {sc.SetState(SCE_B_DEFAULT);}
+ break;
+ }
+ case SCE_B_STRING:
+ {
+ if (sc.ch == '\"'){sc.ForwardSetState(SCE_B_DEFAULT);}
+ break;
+ }
+ case SCE_B_CONSTANT:
+ {
+ if (!IsAWordChar(sc.ch)) {sc.SetState(SCE_B_DEFAULT);}
+ break;
+ }
+ case SCE_B_COMMENT:
+ {
+ if (sc.atLineEnd) {sc.SetState(SCE_B_DEFAULT);}
+ break;
+ }
+ case SCE_B_ASM:
+ {
+ if (sc.atLineEnd) {sc.SetState(SCE_B_DEFAULT);}
+ break;
+ }
+ } //switch (sc.state)
+
+ // Determine if a new state should be entered:
+ if (sc.state == SCE_B_DEFAULT)
+ {
+ if (sc.ch == '\'') {sc.SetState(SCE_B_COMMENT);}
+ else if (sc.ch == '\"') {sc.SetState(SCE_B_STRING);}
+ else if (sc.ch == '&' && tolower(sc.chNext) == 'h') {sc.SetState(SCE_B_NUMBER);}
+ else if (sc.ch == '&' && tolower(sc.chNext) == 'b') {sc.SetState(SCE_B_NUMBER);}
+ else if (sc.ch == '&' && tolower(sc.chNext) == 'o') {sc.SetState(SCE_B_NUMBER);}
+ else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {sc.SetState(SCE_B_NUMBER);}
+ else if (IsAWordStart(sc.ch)) {sc.SetState(SCE_B_KEYWORD);}
+ else if (sc.ch == '%') {sc.SetState(SCE_B_CONSTANT);}
+ else if (sc.ch == '$') {sc.SetState(SCE_B_CONSTANT);}
+ else if (sc.ch == '#') {sc.SetState(SCE_B_KEYWORD);}
+ else if (sc.ch == '!') {sc.SetState(SCE_B_ASM);}
+ else if (isoperator(static_cast<char>(sc.ch)) || (sc.ch == '\\')) {sc.SetState(SCE_B_OPERATOR);}
+ }
+ } //for (; sc.More(); sc.Forward())
+ sc.Complete();
+}
+
+//The folding routine for PowerBasic toggles SUBs and FUNCTIONs only. This was exactly what I wanted,
+//nothing more. I had worked with this kind of toggling for several years when I used the great good old
+//GFA Basic which is dead now. After testing the feature of toggling FOR-NEXT loops, WHILE-WEND loops
+//and so on too I found this is more disturbing then helping (for me). So if You think in another way
+//you can (or must) write Your own toggling routine ;-)
+static void FoldPBDoc(unsigned int startPos, int length, int, WordList *[], Accessor &styler)
+{
+ // No folding enabled, no reason to continue...
+ if( styler.GetPropertyInt("fold") == 0 )
+ return;
+
+ unsigned int endPos = startPos + length;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelCurrent = SC_FOLDLEVELBASE;
+ if (lineCurrent > 0)
+ levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
+ int levelNext = levelCurrent;
+ char chNext = styler[startPos];
+
+ bool fNewLine=true;
+ bool fMightBeMultiLineMacro=false;
+ bool fBeginOfCommentFound=false;
+ for (unsigned int i = startPos; i < endPos; i++)
+ {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+
+ if (fNewLine) //Begin of a new line (The Sub/Function/Macro keywords may occur at begin of line only)
+ {
+ fNewLine=false;
+ fBeginOfCommentFound=false;
+ switch (ch)
+ {
+ case ' ': //Most lines start with space - so check this first, the code is the same as for 'default:'
+ case '\t': //Handle tab too
+ {
+ int levelUse = levelCurrent;
+ int lev = levelUse | levelNext << 16;
+ styler.SetLevel(lineCurrent, lev);
+ break;
+ }
+ case 'F':
+ case 'f':
+ {
+ switch (chNext)
+ {
+ case 'U':
+ case 'u':
+ {
+ if( MatchUpperCase(styler,i,"FUNCTION") )
+ {
+ styler.SetLevel(lineCurrent, (SC_FOLDLEVELBASE << 16) | SC_FOLDLEVELHEADERFLAG);
+ levelNext=SC_FOLDLEVELBASE+1;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case 'S':
+ case 's':
+ {
+ switch (chNext)
+ {
+ case 'U':
+ case 'u':
+ {
+ if( MatchUpperCase(styler,i,"SUB") )
+ {
+ styler.SetLevel(lineCurrent, (SC_FOLDLEVELBASE << 16) | SC_FOLDLEVELHEADERFLAG);
+ levelNext=SC_FOLDLEVELBASE+1;
+ }
+ break;
+ }
+ case 'T':
+ case 't':
+ {
+ if( MatchUpperCase(styler,i,"STATIC FUNCTION") )
+ {
+ styler.SetLevel(lineCurrent, (SC_FOLDLEVELBASE << 16) | SC_FOLDLEVELHEADERFLAG);
+ levelNext=SC_FOLDLEVELBASE+1;
+ }
+ else if( MatchUpperCase(styler,i,"STATIC SUB") )
+ {
+ styler.SetLevel(lineCurrent, (SC_FOLDLEVELBASE << 16) | SC_FOLDLEVELHEADERFLAG);
+ levelNext=SC_FOLDLEVELBASE+1;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case 'C':
+ case 'c':
+ {
+ switch (chNext)
+ {
+ case 'A':
+ case 'a':
+ {
+ if( MatchUpperCase(styler,i,"CALLBACK FUNCTION") )
+ {
+ styler.SetLevel(lineCurrent, (SC_FOLDLEVELBASE << 16) | SC_FOLDLEVELHEADERFLAG);
+ levelNext=SC_FOLDLEVELBASE+1;
+ }
+ break;
+ }
+ }
+ break;
+ }
+ case 'M':
+ case 'm':
+ {
+ switch (chNext)
+ {
+ case 'A':
+ case 'a':
+ {
+ if( MatchUpperCase(styler,i,"MACRO") )
+ {
+ fMightBeMultiLineMacro=true; //Set folder level at end of line, we have to check for single line macro
+ }
+ break;
+ }
+ }
+ break;
+ }
+ default:
+ {
+ int levelUse = levelCurrent;
+ int lev = levelUse | levelNext << 16;
+ styler.SetLevel(lineCurrent, lev);
+ break;
+ }
+ } //switch (ch)
+ } //if( fNewLine )
+
+ switch (ch)
+ {
+ case '=': //To test single line macros
+ {
+ if (fBeginOfCommentFound==false)
+ fMightBeMultiLineMacro=false; //The found macro is a single line macro only;
+ break;
+ }
+ case '\'': //A comment starts
+ {
+ fBeginOfCommentFound=true;
+ break;
+ }
+ case '\n':
+ {
+ if (fMightBeMultiLineMacro) //The current line is the begin of a multi line macro
+ {
+ fMightBeMultiLineMacro=false;
+ styler.SetLevel(lineCurrent, (SC_FOLDLEVELBASE << 16) | SC_FOLDLEVELHEADERFLAG);
+ levelNext=SC_FOLDLEVELBASE+1;
+ }
+ lineCurrent++;
+ levelCurrent = levelNext;
+ fNewLine=true;
+ break;
+ }
+ case '\r':
+ {
+ if (chNext != '\n')
+ {
+ lineCurrent++;
+ levelCurrent = levelNext;
+ fNewLine=true;
+ }
+ break;
+ }
+ } //switch (ch)
+ } //for (unsigned int i = startPos; i < endPos; i++)
+}
+
+static const char * const pbWordListDesc[] = {
+ "Keywords",
+ 0
+};
+
+LexerModule lmPB(SCLEX_POWERBASIC, ColourisePBDoc, "powerbasic", FoldPBDoc, pbWordListDesc);
diff --git a/src/LexPOV.cpp b/src/LexPOV.cpp
new file mode 100755
index 0000000..5cc05ce
--- /dev/null
+++ b/src/LexPOV.cpp
@@ -0,0 +1,312 @@
+// Scintilla source code edit control
+/** @file LexPOV.cxx
+ ** Lexer for POV-Ray SDL (Persistance of Vision Raytracer, Scene Description Language).
+ ** Written by Philippe Lhoste but this is mostly a derivative of LexCPP...
+ **/
+// Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+// Some points that distinguish from a simple C lexer:
+// Identifiers start only by a character.
+// No line continuation character.
+// Strings are limited to 256 characters.
+// Directives are similar to preprocessor commands,
+// but we match directive keywords and colorize incorrect ones.
+// Block comments can be nested (code stolen from my code in LexLua).
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+static inline bool IsAWordChar(int ch) {
+ return ch < 0x80 && (isalnum(ch) || ch == '_');
+}
+
+static inline bool IsAWordStart(int ch) {
+ return ch < 0x80 && isalpha(ch);
+}
+
+static inline bool IsANumberChar(int ch) {
+ // Not exactly following number definition (several dots are seen as OK, etc.)
+ // but probably enough in most cases.
+ return (ch < 0x80) &&
+ (isdigit(ch) || toupper(ch) == 'E' ||
+ ch == '.' || ch == '-' || ch == '+');
+}
+
+static void ColourisePovDoc(
+ unsigned int startPos,
+ int length,
+ int initStyle,
+ WordList *keywordlists[],
+ Accessor &styler) {
+
+ WordList &keywords1 = *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];
+
+ int currentLine = styler.GetLine(startPos);
+ // Initialize the block comment /* */ nesting level, if we are inside such a comment.
+ int blockCommentLevel = 0;
+ if (initStyle == SCE_POV_COMMENT) {
+ blockCommentLevel = styler.GetLineState(currentLine - 1);
+ }
+
+ // Do not leak onto next line
+ if (initStyle == SCE_POV_STRINGEOL || initStyle == SCE_POV_COMMENTLINE) {
+ initStyle = SCE_POV_DEFAULT;
+ }
+
+ short stringLen = 0;
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ for (; sc.More(); sc.Forward()) {
+ if (sc.atLineEnd) {
+ // Update the line state, so it can be seen by next line
+ currentLine = styler.GetLine(sc.currentPos);
+ if (sc.state == SCE_POV_COMMENT) {
+ // Inside a block comment, we set the line state
+ styler.SetLineState(currentLine, blockCommentLevel);
+ } else {
+ // Reset the line state
+ styler.SetLineState(currentLine, 0);
+ }
+ }
+
+ if (sc.atLineStart && (sc.state == SCE_POV_STRING)) {
+ // Prevent SCE_POV_STRINGEOL from leaking back to previous line
+ sc.SetState(SCE_POV_STRING);
+ }
+
+ // Determine if the current state should terminate.
+ if (sc.state == SCE_POV_OPERATOR) {
+ sc.SetState(SCE_POV_DEFAULT);
+ } else if (sc.state == SCE_POV_NUMBER) {
+ // We stop the number definition on non-numerical non-dot non-eE non-sign char
+ if (!IsANumberChar(sc.ch)) {
+ sc.SetState(SCE_POV_DEFAULT);
+ }
+ } else if (sc.state == SCE_POV_IDENTIFIER) {
+ if (!IsAWordChar(sc.ch)) {
+ char s[100];
+ sc.GetCurrent(s, sizeof(s));
+ if (keywords2.InList(s)) {
+ sc.ChangeState(SCE_POV_WORD2);
+ } else if (keywords3.InList(s)) {
+ sc.ChangeState(SCE_POV_WORD3);
+ } else if (keywords4.InList(s)) {
+ sc.ChangeState(SCE_POV_WORD4);
+ } else if (keywords5.InList(s)) {
+ sc.ChangeState(SCE_POV_WORD5);
+ } else if (keywords6.InList(s)) {
+ sc.ChangeState(SCE_POV_WORD6);
+ } else if (keywords7.InList(s)) {
+ sc.ChangeState(SCE_POV_WORD7);
+ } else if (keywords8.InList(s)) {
+ sc.ChangeState(SCE_POV_WORD8);
+ }
+ sc.SetState(SCE_POV_DEFAULT);
+ }
+ } else if (sc.state == SCE_POV_DIRECTIVE) {
+ if (!IsAWordChar(sc.ch)) {
+ char s[100];
+ char *p;
+ sc.GetCurrent(s, sizeof(s));
+ p = s;
+ // Skip # and whitespace between # and directive word
+ do {
+ p++;
+ } while ((*p == ' ' || *p == '\t') && *p != '\0');
+ if (!keywords1.InList(p)) {
+ sc.ChangeState(SCE_POV_BADDIRECTIVE);
+ }
+ sc.SetState(SCE_POV_DEFAULT);
+ }
+ } else if (sc.state == SCE_POV_COMMENT) {
+ if (sc.Match('/', '*')) {
+ blockCommentLevel++;
+ sc.Forward();
+ } else if (sc.Match('*', '/') && blockCommentLevel > 0) {
+ blockCommentLevel--;
+ sc.Forward();
+ if (blockCommentLevel == 0) {
+ sc.ForwardSetState(SCE_POV_DEFAULT);
+ }
+ }
+ } else if (sc.state == SCE_POV_COMMENTLINE) {
+ if (sc.atLineEnd) {
+ sc.ForwardSetState(SCE_POV_DEFAULT);
+ }
+ } else if (sc.state == SCE_POV_STRING) {
+ if (sc.ch == '\\') {
+ stringLen++;
+ if (strchr("abfnrtuv0'\"", sc.chNext)) {
+ // Compound characters are counted as one.
+ // Note: for Unicode chars \u, we shouldn't count the next 4 digits...
+ sc.Forward();
+ }
+ } else if (sc.ch == '\"') {
+ sc.ForwardSetState(SCE_POV_DEFAULT);
+ } else if (sc.atLineEnd) {
+ sc.ChangeState(SCE_POV_STRINGEOL);
+ sc.ForwardSetState(SCE_POV_DEFAULT);
+ } else {
+ stringLen++;
+ }
+ if (stringLen > 256) {
+ // Strings are limited to 256 chars
+ sc.SetState(SCE_POV_STRINGEOL);
+ }
+ } else if (sc.state == SCE_POV_STRINGEOL) {
+ if (sc.ch == '\\') {
+ if (sc.chNext == '\"' || sc.chNext == '\\') {
+ sc.Forward();
+ }
+ } else if (sc.ch == '\"') {
+ sc.ForwardSetState(SCE_C_DEFAULT);
+ } else if (sc.atLineEnd) {
+ sc.ForwardSetState(SCE_POV_DEFAULT);
+ }
+ }
+
+ // Determine if a new state should be entered.
+ if (sc.state == SCE_POV_DEFAULT) {
+ if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
+ sc.SetState(SCE_POV_NUMBER);
+ } else if (IsAWordStart(sc.ch)) {
+ sc.SetState(SCE_POV_IDENTIFIER);
+ } else if (sc.Match('/', '*')) {
+ blockCommentLevel = 1;
+ sc.SetState(SCE_POV_COMMENT);
+ sc.Forward(); // Eat the * so it isn't used for the end of the comment
+ } else if (sc.Match('/', '/')) {
+ sc.SetState(SCE_POV_COMMENTLINE);
+ } else if (sc.ch == '\"') {
+ sc.SetState(SCE_POV_STRING);
+ stringLen = 0;
+ } else if (sc.ch == '#') {
+ sc.SetState(SCE_POV_DIRECTIVE);
+ // Skip whitespace between # and directive word
+ do {
+ sc.Forward();
+ } while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_POV_DEFAULT);
+ }
+ } else if (isoperator(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_POV_OPERATOR);
+ }
+ }
+ }
+ sc.Complete();
+}
+
+static void FoldPovDoc(
+ unsigned int startPos,
+ int length,
+ int initStyle,
+ WordList *[],
+ Accessor &styler) {
+
+ bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
+ bool foldDirective = styler.GetPropertyInt("fold.directive") != 0;
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ unsigned int endPos = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
+ int levelCurrent = levelPrev;
+ char chNext = styler[startPos];
+ int styleNext = styler.StyleAt(startPos);
+ int style = initStyle;
+ for (unsigned int i = startPos; i < endPos; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ int stylePrev = style;
+ style = styleNext;
+ styleNext = styler.StyleAt(i + 1);
+ bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
+ if (foldComment && (style == SCE_POV_COMMENT)) {
+ if (stylePrev != SCE_POV_COMMENT) {
+ levelCurrent++;
+ } else if ((styleNext != SCE_POV_COMMENT) && !atEOL) {
+ // Comments don't end at end of line and the next character may be unstyled.
+ levelCurrent--;
+ }
+ }
+ if (foldComment && (style == SCE_POV_COMMENTLINE)) {
+ if ((ch == '/') && (chNext == '/')) {
+ char chNext2 = styler.SafeGetCharAt(i + 2);
+ if (chNext2 == '{') {
+ levelCurrent++;
+ } else if (chNext2 == '}') {
+ levelCurrent--;
+ }
+ }
+ }
+ if (foldDirective && (style == SCE_POV_DIRECTIVE)) {
+ if (ch == '#') {
+ unsigned int j=i+1;
+ while ((j<endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) {
+ j++;
+ }
+ }
+ }
+ if (style == SCE_POV_OPERATOR) {
+ if (ch == '{') {
+ levelCurrent++;
+ } else if (ch == '}') {
+ levelCurrent--;
+ }
+ }
+ if (atEOL) {
+ int lev = levelPrev;
+ if (visibleChars == 0 && foldCompact)
+ 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 povWordLists[] = {
+ "Language directives",
+ "Objects & CSG & Appearance",
+ "Types & Modifiers & Items",
+ "Predefined Identifiers",
+ "Predefined Functions",
+ "User defined 1",
+ "User defined 2",
+ "User defined 3",
+ 0,
+};
+
+LexerModule lmPOV(SCLEX_POV, ColourisePovDoc, "pov", FoldPovDoc, povWordLists);
diff --git a/src/LexPS.cpp b/src/LexPS.cpp
new file mode 100755
index 0000000..3c75ae5
--- /dev/null
+++ b/src/LexPS.cpp
@@ -0,0 +1,343 @@
+// Scintilla source code edit control
+/** @file LexPS.cxx
+ ** Lexer for PostScript
+ **
+ ** Written by Nigel Hathaway <nigel@bprj.co.uk>.
+ ** The License.txt file describes the conditions under which this software may be distributed.
+ **/
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+static inline bool IsASelfDelimitingChar(const int ch) {
+ return (ch == '[' || ch == ']' || ch == '{' || ch == '}' ||
+ ch == '/' || ch == '<' || ch == '>' ||
+ ch == '(' || ch == ')' || ch == '%');
+}
+
+static inline bool IsAWhitespaceChar(const int ch) {
+ return (ch == ' ' || ch == '\t' || ch == '\r' ||
+ ch == '\n' || ch == '\f' || ch == '\0');
+}
+
+static bool IsABaseNDigit(const int ch, const int base) {
+ int maxdig = '9';
+ int letterext = -1;
+
+ if (base <= 10)
+ maxdig = '0' + base - 1;
+ else
+ letterext = base - 11;
+
+ return ((ch >= '0' && ch <= maxdig) ||
+ (ch >= 'A' && ch <= ('A' + letterext)) ||
+ (ch >= 'a' && ch <= ('a' + letterext)));
+}
+
+static inline bool IsABase85Char(const int ch) {
+ return ((ch >= '!' && ch <= 'u') || ch == 'z');
+}
+
+static void ColourisePSDoc(
+ unsigned int startPos,
+ int length,
+ int initStyle,
+ WordList *keywordlists[],
+ Accessor &styler) {
+
+ WordList &keywords1 = *keywordlists[0];
+ WordList &keywords2 = *keywordlists[1];
+ WordList &keywords3 = *keywordlists[2];
+ WordList &keywords4 = *keywordlists[3];
+ WordList &keywords5 = *keywordlists[4];
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ bool tokenizing = styler.GetPropertyInt("ps.tokenize") != 0;
+ int pslevel = styler.GetPropertyInt("ps.level", 3);
+ int lineCurrent = styler.GetLine(startPos);
+ int nestTextCurrent = 0;
+ if (lineCurrent > 0 && initStyle == SCE_PS_TEXT)
+ nestTextCurrent = styler.GetLineState(lineCurrent - 1);
+ int numRadix = 0;
+ bool numHasPoint = false;
+ bool numHasExponent = false;
+ bool numHasSign = false;
+
+ // Clear out existing tokenization
+ if (tokenizing && length > 0) {
+ styler.StartAt(startPos, static_cast<char>(INDIC2_MASK));
+ styler.ColourTo(startPos + length-1, 0);
+ styler.Flush();
+ styler.StartAt(startPos);
+ styler.StartSegment(startPos);
+ }
+
+ for (; sc.More(); sc.Forward()) {
+ if (sc.atLineStart)
+ lineCurrent = styler.GetLine(sc.currentPos);
+
+ // Determine if the current state should terminate.
+ if (sc.state == SCE_PS_COMMENT || sc.state == SCE_PS_DSC_VALUE) {
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_C_DEFAULT);
+ }
+ } else if (sc.state == SCE_PS_DSC_COMMENT) {
+ if (sc.ch == ':') {
+ sc.Forward();
+ if (!sc.atLineEnd)
+ sc.SetState(SCE_PS_DSC_VALUE);
+ else
+ sc.SetState(SCE_C_DEFAULT);
+ } else if (sc.atLineEnd) {
+ sc.SetState(SCE_C_DEFAULT);
+ } else if (IsAWhitespaceChar(sc.ch)) {
+ sc.ChangeState(SCE_PS_COMMENT);
+ }
+ } else if (sc.state == SCE_PS_NUMBER) {
+ if (IsASelfDelimitingChar(sc.ch) || IsAWhitespaceChar(sc.ch)) {
+ if ((sc.chPrev == '+' || sc.chPrev == '-' ||
+ sc.chPrev == 'E' || sc.chPrev == 'e') && numRadix == 0)
+ sc.ChangeState(SCE_PS_NAME);
+ sc.SetState(SCE_C_DEFAULT);
+ } else if (sc.ch == '#') {
+ if (numHasPoint || numHasExponent || numHasSign || numRadix != 0) {
+ sc.ChangeState(SCE_PS_NAME);
+ } else {
+ char szradix[5];
+ sc.GetCurrent(szradix, 4);
+ numRadix = atoi(szradix);
+ if (numRadix < 2 || numRadix > 36)
+ sc.ChangeState(SCE_PS_NAME);
+ }
+ } else if ((sc.ch == 'E' || sc.ch == 'e') && numRadix == 0) {
+ if (numHasExponent) {
+ sc.ChangeState(SCE_PS_NAME);
+ } else {
+ numHasExponent = true;
+ if (sc.chNext == '+' || sc.chNext == '-')
+ sc.Forward();
+ }
+ } else if (sc.ch == '.') {
+ if (numHasPoint || numHasExponent || numRadix != 0) {
+ sc.ChangeState(SCE_PS_NAME);
+ } else {
+ numHasPoint = true;
+ }
+ } else if (numRadix == 0) {
+ if (!IsABaseNDigit(sc.ch, 10))
+ sc.ChangeState(SCE_PS_NAME);
+ } else {
+ if (!IsABaseNDigit(sc.ch, numRadix))
+ sc.ChangeState(SCE_PS_NAME);
+ }
+ } else if (sc.state == SCE_PS_NAME || sc.state == SCE_PS_KEYWORD) {
+ if (IsASelfDelimitingChar(sc.ch) || IsAWhitespaceChar(sc.ch)) {
+ char s[100];
+ sc.GetCurrent(s, sizeof(s));
+ if ((pslevel >= 1 && keywords1.InList(s)) ||
+ (pslevel >= 2 && keywords2.InList(s)) ||
+ (pslevel >= 3 && keywords3.InList(s)) ||
+ keywords4.InList(s) || keywords5.InList(s)) {
+ sc.ChangeState(SCE_PS_KEYWORD);
+ }
+ sc.SetState(SCE_C_DEFAULT);
+ }
+ } else if (sc.state == SCE_PS_LITERAL || sc.state == SCE_PS_IMMEVAL) {
+ if (IsASelfDelimitingChar(sc.ch) || IsAWhitespaceChar(sc.ch))
+ sc.SetState(SCE_C_DEFAULT);
+ } else if (sc.state == SCE_PS_PAREN_ARRAY || sc.state == SCE_PS_PAREN_DICT ||
+ sc.state == SCE_PS_PAREN_PROC) {
+ sc.SetState(SCE_C_DEFAULT);
+ } else if (sc.state == SCE_PS_TEXT) {
+ if (sc.ch == '(') {
+ nestTextCurrent++;
+ } else if (sc.ch == ')') {
+ if (--nestTextCurrent == 0)
+ sc.ForwardSetState(SCE_PS_DEFAULT);
+ } else if (sc.ch == '\\') {
+ sc.Forward();
+ }
+ } else if (sc.state == SCE_PS_HEXSTRING) {
+ if (sc.ch == '>') {
+ sc.ForwardSetState(SCE_PS_DEFAULT);
+ } else if (!IsABaseNDigit(sc.ch, 16) && !IsAWhitespaceChar(sc.ch)) {
+ sc.SetState(SCE_PS_HEXSTRING);
+ styler.ColourTo(sc.currentPos, SCE_PS_BADSTRINGCHAR);
+ }
+ } else if (sc.state == SCE_PS_BASE85STRING) {
+ if (sc.Match('~', '>')) {
+ sc.Forward();
+ sc.ForwardSetState(SCE_PS_DEFAULT);
+ } else if (!IsABase85Char(sc.ch) && !IsAWhitespaceChar(sc.ch)) {
+ sc.SetState(SCE_PS_BASE85STRING);
+ styler.ColourTo(sc.currentPos, SCE_PS_BADSTRINGCHAR);
+ }
+ }
+
+ // Determine if a new state should be entered.
+ if (sc.state == SCE_C_DEFAULT) {
+ unsigned int tokenpos = sc.currentPos;
+
+ if (sc.ch == '[' || sc.ch == ']') {
+ sc.SetState(SCE_PS_PAREN_ARRAY);
+ } else if (sc.ch == '{' || sc.ch == '}') {
+ sc.SetState(SCE_PS_PAREN_PROC);
+ } else if (sc.ch == '/') {
+ if (sc.chNext == '/') {
+ sc.SetState(SCE_PS_IMMEVAL);
+ sc.Forward();
+ } else {
+ sc.SetState(SCE_PS_LITERAL);
+ }
+ } else if (sc.ch == '<') {
+ if (sc.chNext == '<') {
+ sc.SetState(SCE_PS_PAREN_DICT);
+ sc.Forward();
+ } else if (sc.chNext == '~') {
+ sc.SetState(SCE_PS_BASE85STRING);
+ sc.Forward();
+ } else {
+ sc.SetState(SCE_PS_HEXSTRING);
+ }
+ } else if (sc.ch == '>' && sc.chNext == '>') {
+ sc.SetState(SCE_PS_PAREN_DICT);
+ sc.Forward();
+ } else if (sc.ch == '>' || sc.ch == ')') {
+ sc.SetState(SCE_C_DEFAULT);
+ styler.ColourTo(sc.currentPos, SCE_PS_BADSTRINGCHAR);
+ } else if (sc.ch == '(') {
+ sc.SetState(SCE_PS_TEXT);
+ nestTextCurrent = 1;
+ } else if (sc.ch == '%') {
+ if (sc.chNext == '%' && sc.atLineStart) {
+ sc.SetState(SCE_PS_DSC_COMMENT);
+ sc.Forward();
+ if (sc.chNext == '+') {
+ sc.Forward();
+ sc.ForwardSetState(SCE_PS_DSC_VALUE);
+ }
+ } else {
+ sc.SetState(SCE_PS_COMMENT);
+ }
+ } else if ((sc.ch == '+' || sc.ch == '-' || sc.ch == '.') &&
+ IsABaseNDigit(sc.chNext, 10)) {
+ sc.SetState(SCE_PS_NUMBER);
+ numRadix = 0;
+ numHasPoint = (sc.ch == '.');
+ numHasExponent = false;
+ numHasSign = (sc.ch == '+' || sc.ch == '-');
+ } else if ((sc.ch == '+' || sc.ch == '-') && sc.chNext == '.' &&
+ IsABaseNDigit(sc.GetRelative(2), 10)) {
+ sc.SetState(SCE_PS_NUMBER);
+ numRadix = 0;
+ numHasPoint = false;
+ numHasExponent = false;
+ numHasSign = true;
+ } else if (IsABaseNDigit(sc.ch, 10)) {
+ sc.SetState(SCE_PS_NUMBER);
+ numRadix = 0;
+ numHasPoint = false;
+ numHasExponent = false;
+ numHasSign = false;
+ } else if (!IsAWhitespaceChar(sc.ch)) {
+ sc.SetState(SCE_PS_NAME);
+ }
+
+ // Mark the start of tokens
+ if (tokenizing && sc.state != SCE_C_DEFAULT && sc.state != SCE_PS_COMMENT &&
+ sc.state != SCE_PS_DSC_COMMENT && sc.state != SCE_PS_DSC_VALUE) {
+ styler.Flush();
+ styler.StartAt(tokenpos, static_cast<char>(INDIC2_MASK));
+ styler.ColourTo(tokenpos, INDIC2_MASK);
+ styler.Flush();
+ styler.StartAt(tokenpos);
+ styler.StartSegment(tokenpos);
+ }
+ }
+
+ if (sc.atLineEnd)
+ styler.SetLineState(lineCurrent, nestTextCurrent);
+ }
+
+ sc.Complete();
+}
+
+static void FoldPSDoc(unsigned int startPos, int length, int, WordList *[],
+ Accessor &styler) {
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0;
+ unsigned int endPos = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelCurrent = SC_FOLDLEVELBASE;
+ if (lineCurrent > 0)
+ levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
+ int levelMinCurrent = levelCurrent;
+ int levelNext = levelCurrent;
+ char chNext = styler[startPos];
+ int styleNext = styler.StyleAt(startPos);
+ int style;
+ for (unsigned int i = startPos; i < endPos; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ style = styleNext;
+ styleNext = styler.StyleAt(i + 1);
+ bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n'); //mac??
+ if ((style & 31) == SCE_PS_PAREN_PROC) {
+ if (ch == '{') {
+ // Measure the minimum before a '{' to allow
+ // folding on "} {"
+ if (levelMinCurrent > levelNext) {
+ levelMinCurrent = levelNext;
+ }
+ levelNext++;
+ } else if (ch == '}') {
+ levelNext--;
+ }
+ }
+ if (atEOL) {
+ int levelUse = levelCurrent;
+ if (foldAtElse) {
+ levelUse = levelMinCurrent;
+ }
+ int lev = levelUse | levelNext << 16;
+ if (visibleChars == 0 && foldCompact)
+ lev |= SC_FOLDLEVELWHITEFLAG;
+ if (levelUse < levelNext)
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ if (lev != styler.LevelAt(lineCurrent)) {
+ styler.SetLevel(lineCurrent, lev);
+ }
+ lineCurrent++;
+ levelCurrent = levelNext;
+ levelMinCurrent = levelCurrent;
+ visibleChars = 0;
+ }
+ if (!isspacechar(ch))
+ visibleChars++;
+ }
+}
+
+static const char * const psWordListDesc[] = {
+ "PS Level 1 operators",
+ "PS Level 2 operators",
+ "PS Level 3 operators",
+ "RIP-specific operators",
+ "User-defined operators",
+ 0
+};
+
+LexerModule lmPS(SCLEX_PS, ColourisePSDoc, "ps", FoldPSDoc, psWordListDesc);
diff --git a/src/LexPascal.cpp b/src/LexPascal.cpp
new file mode 100755
index 0000000..434f88d
--- /dev/null
+++ b/src/LexPascal.cpp
@@ -0,0 +1,369 @@
+// Scintilla source code edit control
+/** @file LexPascal.cxx
+ ** Lexer for Pascal.
+ ** Written by Laurent le Tynevez
+ ** Updated by Simon Steele <s.steele@pnotepad.org> September 2002
+ ** Updated by Mathias Rauen <scite@madshi.net> May 2003 (Delphi adjustments)
+ **/
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+#include "StyleContext.h"
+
+static void getRange(unsigned int start,
+ unsigned int end,
+ Accessor &styler,
+ char *s,
+ unsigned int len) {
+ unsigned int i = 0;
+ while ((i < end - start + 1) && (i < len-1)) {
+ s[i] = static_cast<char>(tolower(styler[start + i]));
+ i++;
+ }
+ s[i] = '\0';
+}
+
+static bool IsStreamCommentStyle(int style) {
+ return style == SCE_C_COMMENT ||
+ style == SCE_C_COMMENTDOC ||
+ style == SCE_C_COMMENTDOCKEYWORD ||
+ style == SCE_C_COMMENTDOCKEYWORDERROR;
+}
+
+static void ColourTo(Accessor &styler, unsigned int end, unsigned int attr, bool bInAsm) {
+ if ((bInAsm) && (attr == SCE_C_OPERATOR || attr == SCE_C_NUMBER || attr == SCE_C_DEFAULT || attr == SCE_C_WORD || attr == SCE_C_IDENTIFIER)) {
+ styler.ColourTo(end, SCE_C_REGEX);
+ } else
+ styler.ColourTo(end, attr);
+}
+
+// returns 1 if the item starts a class definition, and -1 if the word is "end", and 2 if the word is "asm"
+static int classifyWordPascal(unsigned int start, unsigned int end, /*WordList &keywords*/WordList *keywordlists[], Accessor &styler, bool bInClass, bool bInAsm) {
+ int ret = 0;
+
+ WordList& keywords = *keywordlists[0];
+ WordList& classwords = *keywordlists[1];
+
+ char s[100];
+ getRange(start, end, styler, s, sizeof(s));
+
+ char chAttr = SCE_C_IDENTIFIER;
+ if (isdigit(s[0]) || (s[0] == '.') ||(s[0] == '$')) {
+ chAttr = SCE_C_NUMBER;
+ }
+ else {
+ if (s[0] == '#') {
+ chAttr = SCE_C_CHARACTER;
+ }
+ else {
+ if (keywords.InList(s)) {
+ chAttr = SCE_C_WORD;
+
+ if(strcmp(s, "class") == 0) {
+ ret = 1;
+ }
+ else if (strcmp(s, "asm") == 0) {
+ ret = 2;
+ }
+ else if (strcmp(s, "end") == 0) {
+ ret = -1;
+ }
+ } else if (bInClass) {
+ if (classwords.InList(s)) {
+ chAttr = SCE_C_WORD;
+ }
+ }
+ }
+ }
+ ColourTo(styler, end, chAttr, (bInAsm && ret != -1));
+ return ret;
+}
+
+static int classifyFoldPointPascal(const char* s) {
+ int lev = 0;
+ if (!(isdigit(s[0]) || (s[0] == '.'))) {
+ if (strcmp(s, "begin") == 0 ||
+ strcmp(s, "object") == 0 ||
+ strcmp(s, "case") == 0 ||
+ strcmp(s, "class") == 0 ||
+ strcmp(s, "record") == 0 ||
+ strcmp(s, "try") == 0) {
+ lev=1;
+ } else if (strcmp(s, "end") == 0) {
+ lev=-1;
+ }
+ }
+ return lev;
+}
+
+static void ColourisePascalDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+ Accessor &styler) {
+
+ styler.StartAt(startPos);
+
+ int state = initStyle;
+ if (state == SCE_C_CHARACTER) // Does not leak onto next line
+ state = SCE_C_DEFAULT;
+ char chPrev = ' ';
+ char chNext = styler[startPos];
+ unsigned int lengthDoc = startPos + length;
+
+ bool bInClassDefinition;
+
+ int currentLine = styler.GetLine(startPos);
+ if (currentLine > 0) {
+ styler.SetLineState(currentLine, styler.GetLineState(currentLine-1));
+ bInClassDefinition = (styler.GetLineState(currentLine) == 1);
+ } else {
+ styler.SetLineState(currentLine, 0);
+ bInClassDefinition = false;
+ }
+
+ bool bInAsm = (state == SCE_C_REGEX);
+ if (bInAsm)
+ state = SCE_C_DEFAULT;
+
+ styler.StartSegment(startPos);
+ for (unsigned int i = startPos; i < lengthDoc; i++) {
+ char ch = chNext;
+
+ chNext = styler.SafeGetCharAt(i + 1);
+
+ if ((ch == '\r' && chNext != '\n') || (ch == '\n')) {
+ // Trigger on CR only (Mac style) or either on LF from CR+LF (Dos/Win) or on LF alone (Unix)
+ // Avoid triggering two times on Dos/Win
+ // End of line
+ if (state == SCE_C_CHARACTER) {
+ ColourTo(styler, i, state, bInAsm);
+ state = SCE_C_DEFAULT;
+ }
+ currentLine++;
+ styler.SetLineState(currentLine, (bInClassDefinition ? 1 : 0));
+ }
+
+ if (styler.IsLeadByte(ch)) {
+ chNext = styler.SafeGetCharAt(i + 2);
+ chPrev = ' ';
+ i += 1;
+ continue;
+ }
+
+ if (state == SCE_C_DEFAULT) {
+ if (iswordstart(ch) || ch == '#' || ch == '$' || (ch == '@' && bInAsm)) {
+ ColourTo(styler, i-1, state, bInAsm);
+ state = SCE_C_IDENTIFIER;
+ } else if (ch == '{' && chNext != '$' && chNext != '&') {
+ ColourTo(styler, i-1, state, bInAsm);
+ state = SCE_C_COMMENT;
+ } else if (ch == '(' && chNext == '*'
+ && styler.SafeGetCharAt(i + 2) != '$'
+ && styler.SafeGetCharAt(i + 2) != '&') {
+ ColourTo(styler, i-1, state, bInAsm);
+ state = SCE_C_COMMENTDOC;
+ } else if (ch == '/' && chNext == '/') {
+ ColourTo(styler, i-1, state, bInAsm);
+ state = SCE_C_COMMENTLINE;
+ } else if (ch == '\'') {
+ ColourTo(styler, i-1, state, bInAsm);
+ state = SCE_C_CHARACTER;
+ } else if (ch == '{' && (chNext == '$' || chNext=='&')) {
+ ColourTo(styler, i-1, state, bInAsm);
+ state = SCE_C_PREPROCESSOR;
+ } else if (isoperator(ch)) {
+ ColourTo(styler, i-1, state, bInAsm);
+ ColourTo(styler, i, SCE_C_OPERATOR, bInAsm);
+
+ }
+ } else if (state == SCE_C_IDENTIFIER) {
+ bool bDoublePoint = ((ch == '.') && (chPrev == '.'));
+ if ((!iswordchar(ch) && ch != '$' && ch != '#' && (ch != '@' || !bInAsm)) || bDoublePoint) {
+ if (bDoublePoint) i--;
+ int lStateChange = classifyWordPascal(styler.GetStartSegment(), i - 1, keywordlists, styler, bInClassDefinition, bInAsm);
+
+ if(lStateChange == 1) {
+ styler.SetLineState(currentLine, 1);
+ bInClassDefinition = true;
+ } else if(lStateChange == 2) {
+ bInAsm = true;
+ } else if(lStateChange == -1) {
+ styler.SetLineState(currentLine, 0);
+ bInClassDefinition = false;
+ bInAsm = false;
+ }
+ if (bDoublePoint) {
+ i++;
+ ColourTo(styler, i-1, SCE_C_DEFAULT, bInAsm);
+ }
+
+ state = SCE_C_DEFAULT;
+ chNext = styler.SafeGetCharAt(i + 1);
+ if (ch == '{' && chNext != '$' && chNext != '&') {
+ state = SCE_C_COMMENT;
+ } else if (ch == '(' && chNext == '*'
+ && styler.SafeGetCharAt(i + 2) != '$'
+ && styler.SafeGetCharAt(i + 2) != '&') {
+ ColourTo(styler, i-1, state, bInAsm);
+ state = SCE_C_COMMENTDOC;
+ } else if (ch == '/' && chNext == '/') {
+ state = SCE_C_COMMENTLINE;
+ } else if (ch == '\'') {
+ state = SCE_C_CHARACTER;
+ } else if (isoperator(ch)) {
+ ColourTo(styler, i, SCE_C_OPERATOR, bInAsm);
+ }
+ }
+ } else {
+ if (state == SCE_C_PREPROCESSOR) {
+ if (ch=='}'){
+ ColourTo(styler, i, state, bInAsm);
+ state = SCE_C_DEFAULT;
+ } else {
+ if ((ch == '\r' || ch == '\n') && !(chPrev == '\\' || chPrev == '\r')) {
+ ColourTo(styler, i-1, state, bInAsm);
+ state = SCE_C_DEFAULT;
+ }
+ }
+ } else if (state == SCE_C_COMMENT) {
+ if (ch == '}' ) {
+ ColourTo(styler, i, state, bInAsm);
+ state = SCE_C_DEFAULT;
+ }
+ } else if (state == SCE_C_COMMENTDOC) {
+ if (ch == ')' && chPrev == '*') {
+ if (((i > styler.GetStartSegment() + 2) || (
+ (initStyle == SCE_C_COMMENTDOC) &&
+ (styler.GetStartSegment() == static_cast<unsigned int>(startPos))))) {
+ ColourTo(styler, i, state, bInAsm);
+ state = SCE_C_DEFAULT;
+ }
+ }
+ } else if (state == SCE_C_COMMENTLINE) {
+ if (ch == '\r' || ch == '\n') {
+ ColourTo(styler, i-1, state, bInAsm);
+ state = SCE_C_DEFAULT;
+ }
+ } else if (state == SCE_C_CHARACTER) {
+ if (ch == '\'') {
+ ColourTo(styler, i, state, bInAsm);
+ state = SCE_C_DEFAULT;
+ }
+ }
+ }
+ chPrev = ch;
+ }
+ ColourTo(styler, lengthDoc - 1, state, bInAsm);
+}
+
+static void FoldPascalDoc(unsigned int startPos, int length, int initStyle, WordList *[],
+ Accessor &styler) {
+ bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
+ bool foldPreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0;
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ unsigned int endPos = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
+ int levelCurrent = levelPrev;
+ char chNext = styler[startPos];
+ int styleNext = styler.StyleAt(startPos);
+ int style = initStyle;
+
+ int lastStart = 0;
+
+ for (unsigned int i = startPos; i < endPos; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ int stylePrev = style;
+ style = styleNext;
+ styleNext = styler.StyleAt(i + 1);
+ bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
+
+ if (stylePrev == SCE_C_DEFAULT && style == SCE_C_WORD)
+ {
+ // Store last word start point.
+ lastStart = i;
+ }
+
+ if (stylePrev == SCE_C_WORD) {
+ if(iswordchar(ch) && !iswordchar(chNext)) {
+ char s[100];
+ getRange(lastStart, i, styler, s, sizeof(s));
+ levelCurrent += classifyFoldPointPascal(s);
+ }
+ }
+
+ if (foldComment && (style == SCE_C_COMMENTLINE)) {
+ if ((ch == '/') && (chNext == '/')) {
+ char chNext2 = styler.SafeGetCharAt(i + 2);
+ if (chNext2 == '{') {
+ levelCurrent++;
+ } else if (chNext2 == '}') {
+ levelCurrent--;
+ }
+ }
+ }
+
+ if (foldPreprocessor && (style == SCE_C_PREPROCESSOR)) {
+ if (ch == '{' && chNext == '$') {
+ unsigned int j=i+2; // skip {$
+ while ((j<endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) {
+ j++;
+ }
+ if (styler.Match(j, "region") || styler.Match(j, "if")) {
+ levelCurrent++;
+ } else if (styler.Match(j, "end")) {
+ levelCurrent--;
+ }
+ }
+ }
+
+ if (foldComment && IsStreamCommentStyle(style)) {
+ if (!IsStreamCommentStyle(stylePrev)) {
+ levelCurrent++;
+ } else if (!IsStreamCommentStyle(styleNext) && !atEOL) {
+ // Comments don't end at end of line and the next character may be unstyled.
+ levelCurrent--;
+ }
+ }
+
+ if (atEOL) {
+ int lev = levelPrev;
+ if (visibleChars == 0 && foldCompact)
+ 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 pascalWordListDesc[] = {
+ "Keywords",
+ "Classwords",
+ 0
+};
+
+LexerModule lmPascal(SCLEX_PASCAL, ColourisePascalDoc, "pascal", FoldPascalDoc, pascalWordListDesc);
diff --git a/src/LexPerl.cpp b/src/LexPerl.cpp
new file mode 100755
index 0000000..4a42bc2
--- /dev/null
+++ b/src/LexPerl.cpp
@@ -0,0 +1,1256 @@
+// Scintilla source code edit control
+/** @file LexPerl.cxx
+ ** Lexer for subset of Perl.
+ **/
+// Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
+// Lexical analysis fixes by Kein-Hong Man <mkh@pl.jaring.my>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+#define PERLNUM_BINARY 1 // order is significant: 1-4 cannot have a dot
+#define PERLNUM_HEX 2
+#define PERLNUM_OCTAL 3
+#define PERLNUM_FLOAT 4 // actually exponent part
+#define PERLNUM_DECIMAL 5 // 1-5 are numbers; 6-7 are strings
+#define PERLNUM_VECTOR 6
+#define PERLNUM_V_VECTOR 7
+#define PERLNUM_BAD 8
+
+#define BACK_NONE 0 // lookback state for bareword disambiguation:
+#define BACK_OPERATOR 1 // whitespace/comments are insignificant
+#define BACK_KEYWORD 2 // operators/keywords are needed for disambiguation
+
+#define HERE_DELIM_MAX 256
+
+static inline bool isEOLChar(char ch) {
+ return (ch == '\r') || (ch == '\n');
+}
+
+static bool isSingleCharOp(char ch) {
+ char strCharSet[2];
+ strCharSet[0] = ch;
+ strCharSet[1] = '\0';
+ return (NULL != strstr("rwxoRWXOezsfdlpSbctugkTBMAC", strCharSet));
+}
+
+static inline bool isPerlOperator(char ch) {
+ if (ch == '^' || ch == '&' || ch == '\\' ||
+ ch == '(' || ch == ')' || ch == '-' || ch == '+' ||
+ ch == '=' || ch == '|' || ch == '{' || ch == '}' ||
+ ch == '[' || ch == ']' || ch == ':' || ch == ';' ||
+ ch == '>' || ch == ',' ||
+ ch == '?' || ch == '!' || ch == '.' || ch == '~')
+ return true;
+ // these chars are already tested before this call
+ // ch == '%' || ch == '*' || ch == '<' || ch == '/' ||
+ return false;
+}
+
+static bool isPerlKeyword(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler) {
+ char s[100];
+ unsigned int i, len = end - start;
+ if (len > 30) { len = 30; }
+ for (i = 0; i < len; i++, start++) s[i] = styler[start];
+ s[i] = '\0';
+ return keywords.InList(s);
+}
+
+// Note: as lexer uses chars, UTF-8 bytes are considered as <0 values
+// Note: iswordchar() was used in only one place in LexPerl, it is
+// unnecessary as '.' is processed as the concatenation operator, so
+// only isWordStart() is used in LexPerl
+
+static inline bool isWordStart(char ch) {
+ return !isascii(ch) || isalnum(ch) || ch == '_';
+}
+
+static inline bool isEndVar(char ch) {
+ return isascii(ch) && !isalnum(ch) && ch != '#' && ch != '$' &&
+ ch != '_' && ch != '\'';
+}
+
+static inline bool isNonQuote(char ch) {
+ return !isascii(ch) || isalnum(ch) || ch == '_';
+}
+
+static inline char actualNumStyle(int numberStyle) {
+ if (numberStyle == PERLNUM_VECTOR || numberStyle == PERLNUM_V_VECTOR) {
+ return SCE_PL_STRING;
+ } else if (numberStyle == PERLNUM_BAD) {
+ return SCE_PL_ERROR;
+ }
+ return SCE_PL_NUMBER;
+}
+
+static bool isMatch(Accessor &styler, int lengthDoc, int pos, const char *val) {
+ if ((pos + static_cast<int>(strlen(val))) >= lengthDoc) {
+ return false;
+ }
+ while (*val) {
+ if (*val != styler[pos++]) {
+ return false;
+ }
+ val++;
+ }
+ return true;
+}
+
+static char opposite(char ch) {
+ if (ch == '(')
+ return ')';
+ if (ch == '[')
+ return ']';
+ if (ch == '{')
+ return '}';
+ if (ch == '<')
+ return '>';
+ return ch;
+}
+
+static void ColourisePerlDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler) {
+
+ // Lexer for perl often has to backtrack to start of current style to determine
+ // which characters are being used as quotes, how deeply nested is the
+ // start position and what the termination string is for here documents
+
+ WordList &keywords = *keywordlists[0];
+
+ class HereDocCls {
+ public:
+ int State; // 0: '<<' encountered
+ // 1: collect the delimiter
+ // 2: here doc text (lines after the delimiter)
+ char Quote; // the char after '<<'
+ bool Quoted; // true if Quote in ('\'','"','`')
+ int DelimiterLength; // strlen(Delimiter)
+ char *Delimiter; // the Delimiter, 256: sizeof PL_tokenbuf
+ HereDocCls() {
+ State = 0;
+ Quote = 0;
+ Quoted = false;
+ DelimiterLength = 0;
+ Delimiter = new char[HERE_DELIM_MAX];
+ Delimiter[0] = '\0';
+ }
+ ~HereDocCls() {
+ delete []Delimiter;
+ }
+ };
+ HereDocCls HereDoc; // TODO: FIFO for stacked here-docs
+
+ class QuoteCls {
+ public:
+ int Rep;
+ int Count;
+ char Up;
+ char Down;
+ QuoteCls() {
+ this->New(1);
+ }
+ void New(int r) {
+ Rep = r;
+ Count = 0;
+ Up = '\0';
+ Down = '\0';
+ }
+ void Open(char u) {
+ Count++;
+ Up = u;
+ Down = opposite(Up);
+ }
+ };
+ QuoteCls Quote;
+
+ int state = initStyle;
+ char numState = PERLNUM_DECIMAL;
+ int dotCount = 0;
+ unsigned int lengthDoc = startPos + length;
+ //int sookedpos = 0; // these have no apparent use, see POD state
+ //char sooked[100];
+ //sooked[sookedpos] = '\0';
+
+ // If in a long distance lexical state, seek to the beginning to find quote characters
+ // Perl strings can be multi-line with embedded newlines, so backtrack.
+ // Perl numbers have additional state during lexing, so backtrack too.
+ if (state == SCE_PL_HERE_Q || state == SCE_PL_HERE_QQ || state == SCE_PL_HERE_QX) {
+ while ((startPos > 1) && (styler.StyleAt(startPos) != SCE_PL_HERE_DELIM)) {
+ startPos--;
+ }
+ startPos = styler.LineStart(styler.GetLine(startPos));
+ state = styler.StyleAt(startPos - 1);
+ }
+ if ( state == SCE_PL_STRING_Q
+ || state == SCE_PL_STRING_QQ
+ || state == SCE_PL_STRING_QX
+ || state == SCE_PL_STRING_QR
+ || state == SCE_PL_STRING_QW
+ || state == SCE_PL_REGEX
+ || state == SCE_PL_REGSUBST
+ || state == SCE_PL_STRING
+ || state == SCE_PL_BACKTICKS
+ || state == SCE_PL_CHARACTER
+ || state == SCE_PL_NUMBER
+ || state == SCE_PL_IDENTIFIER
+ || state == SCE_PL_ERROR
+ ) {
+ while ((startPos > 1) && (styler.StyleAt(startPos - 1) == state)) {
+ startPos--;
+ }
+ state = SCE_PL_DEFAULT;
+ }
+
+ // lookback at start of lexing to set proper state for backflag
+ // after this, they are updated when elements are lexed
+ int backflag = BACK_NONE;
+ unsigned int backPos = startPos;
+ if (backPos > 0) {
+ backPos--;
+ int sty = SCE_PL_DEFAULT;
+ while ((backPos > 0) && (sty = styler.StyleAt(backPos),
+ sty == SCE_PL_DEFAULT || sty == SCE_PL_COMMENTLINE))
+ backPos--;
+ if (sty == SCE_PL_OPERATOR)
+ backflag = BACK_OPERATOR;
+ else if (sty == SCE_PL_WORD)
+ backflag = BACK_KEYWORD;
+ }
+
+ styler.StartAt(startPos);
+ char chPrev = styler.SafeGetCharAt(startPos - 1);
+ if (startPos == 0)
+ chPrev = '\n';
+ char chNext = styler[startPos];
+ styler.StartSegment(startPos);
+
+ for (unsigned int i = startPos; i < lengthDoc; i++) {
+ char ch = chNext;
+ // if the current character is not consumed due to the completion of an
+ // earlier style, lexing can be restarted via a simple goto
+ restartLexer:
+ chNext = styler.SafeGetCharAt(i + 1);
+ char chNext2 = styler.SafeGetCharAt(i + 2);
+
+ if (styler.IsLeadByte(ch)) {
+ chNext = styler.SafeGetCharAt(i + 2);
+ chPrev = ' ';
+ i += 1;
+ continue;
+ }
+ if ((chPrev == '\r' && ch == '\n')) { // skip on DOS/Windows
+ styler.ColourTo(i, state);
+ chPrev = ch;
+ continue;
+ }
+
+ if (HereDoc.State == 1 && isEOLChar(ch)) {
+ // Begin of here-doc (the line after the here-doc delimiter):
+ // Lexically, the here-doc starts from the next line after the >>, but the
+ // first line of here-doc seem to follow the style of the last EOL sequence
+ HereDoc.State = 2;
+ if (HereDoc.Quoted) {
+ if (state == SCE_PL_HERE_DELIM) {
+ // Missing quote at end of string! We are stricter than perl.
+ // Colour here-doc anyway while marking this bit as an error.
+ state = SCE_PL_ERROR;
+ }
+ styler.ColourTo(i - 1, state);
+ switch (HereDoc.Quote) {
+ case '\'':
+ state = SCE_PL_HERE_Q ;
+ break;
+ case '"':
+ state = SCE_PL_HERE_QQ;
+ break;
+ case '`':
+ state = SCE_PL_HERE_QX;
+ break;
+ }
+ } else {
+ styler.ColourTo(i - 1, state);
+ switch (HereDoc.Quote) {
+ case '\\':
+ state = SCE_PL_HERE_Q ;
+ break;
+ default :
+ state = SCE_PL_HERE_QQ;
+ }
+ }
+ }
+
+ if (state == SCE_PL_DEFAULT) {
+ if ((isascii(ch) && isdigit(ch)) || (isascii(chNext) && isdigit(chNext) &&
+ (ch == '.' || ch == 'v'))) {
+ state = SCE_PL_NUMBER;
+ backflag = BACK_NONE;
+ numState = PERLNUM_DECIMAL;
+ dotCount = 0;
+ if (ch == '0') { // hex,bin,octal
+ if (chNext == 'x') {
+ numState = PERLNUM_HEX;
+ } else if (chNext == 'b') {
+ numState = PERLNUM_BINARY;
+ } else if (isascii(chNext) && isdigit(chNext)) {
+ numState = PERLNUM_OCTAL;
+ }
+ if (numState != PERLNUM_DECIMAL) {
+ i++;
+ ch = chNext;
+ chNext = chNext2;
+ }
+ } else if (ch == 'v') { // vector
+ numState = PERLNUM_V_VECTOR;
+ }
+ } else if (isWordStart(ch)) {
+ // if immediately prefixed by '::', always a bareword
+ state = SCE_PL_WORD;
+ if (chPrev == ':' && styler.SafeGetCharAt(i - 2) == ':') {
+ state = SCE_PL_IDENTIFIER;
+ }
+ unsigned int kw = i + 1;
+ // first check for possible quote-like delimiter
+ if (ch == 's' && !isNonQuote(chNext)) {
+ state = SCE_PL_REGSUBST;
+ Quote.New(2);
+ } else if (ch == 'm' && !isNonQuote(chNext)) {
+ state = SCE_PL_REGEX;
+ Quote.New(1);
+ } else if (ch == 'q' && !isNonQuote(chNext)) {
+ state = SCE_PL_STRING_Q;
+ Quote.New(1);
+ } else if (ch == 'y' && !isNonQuote(chNext)) {
+ state = SCE_PL_REGSUBST;
+ Quote.New(2);
+ } else if (ch == 't' && chNext == 'r' && !isNonQuote(chNext2)) {
+ state = SCE_PL_REGSUBST;
+ Quote.New(2);
+ kw++;
+ } else if (ch == 'q' && (chNext == 'q' || chNext == 'r' || chNext == 'w' || chNext == 'x') && !isNonQuote(chNext2)) {
+ if (chNext == 'q') state = SCE_PL_STRING_QQ;
+ else if (chNext == 'x') state = SCE_PL_STRING_QX;
+ else if (chNext == 'r') state = SCE_PL_STRING_QR;
+ else if (chNext == 'w') state = SCE_PL_STRING_QW;
+ Quote.New(1);
+ kw++;
+ } else if (ch == 'x' && (chNext == '=' || // repetition
+ !isWordStart(chNext) ||
+ (isdigit(chPrev) && isdigit(chNext)))) {
+ state = SCE_PL_OPERATOR;
+ }
+ // if potentially a keyword, scan forward and grab word, then check
+ // if it's really one; if yes, disambiguation test is performed
+ // otherwise it is always a bareword and we skip a lot of scanning
+ // note: keywords assumed to be limited to [_a-zA-Z] only
+ if (state == SCE_PL_WORD) {
+ while (isWordStart(styler.SafeGetCharAt(kw))) kw++;
+ if (!isPerlKeyword(styler.GetStartSegment(), kw, keywords, styler)) {
+ state = SCE_PL_IDENTIFIER;
+ }
+ }
+ // if already SCE_PL_IDENTIFIER, then no ambiguity, skip this
+ // for quote-like delimiters/keywords, attempt to disambiguate
+ // to select for bareword, change state -> SCE_PL_IDENTIFIER
+ if (state != SCE_PL_IDENTIFIER && i > 0) {
+ unsigned int j = i;
+ bool moreback = false; // true if passed newline/comments
+ bool brace = false; // true if opening brace found
+ char ch2;
+ // first look backwards past whitespace/comments for EOLs
+ // if BACK_NONE, neither operator nor keyword, so skip test
+ if (backflag != BACK_NONE) {
+ while (--j > backPos) {
+ if (isEOLChar(styler.SafeGetCharAt(j)))
+ moreback = true;
+ }
+ ch2 = styler.SafeGetCharAt(j);
+ if (ch2 == '{' && !moreback) {
+ // {bareword: possible variable spec
+ brace = true;
+ } else if ((ch2 == '&' && styler.SafeGetCharAt(j - 1) != '&')
+ // &bareword: subroutine call
+ || (ch2 == '>' && styler.SafeGetCharAt(j - 1) == '-')
+ // ->bareword: part of variable spec
+ || (ch2 == 'b' && styler.Match(j - 2, "su"))) {
+ // sub bareword: subroutine declaration
+ // (implied BACK_KEYWORD, no keywords end in 'sub'!)
+ state = SCE_PL_IDENTIFIER;
+ }
+ // if status still ambiguous, look forward after word past
+ // tabs/spaces only; if ch2 isn't one of '[{(,' it can never
+ // match anything, so skip the whole thing
+ j = kw;
+ if (state != SCE_PL_IDENTIFIER
+ && (ch2 == '{' || ch2 == '(' || ch2 == '['|| ch2 == ',')
+ && kw < lengthDoc) {
+ while (ch2 = styler.SafeGetCharAt(j),
+ (ch2 == ' ' || ch2 == '\t') && j < lengthDoc) {
+ j++;
+ }
+ if ((ch2 == '}' && brace)
+ // {bareword}: variable spec
+ || (ch2 == '=' && styler.SafeGetCharAt(j + 1) == '>')) {
+ // [{(, bareword=>: hash literal
+ state = SCE_PL_IDENTIFIER;
+ }
+ }
+ }
+ }
+ backflag = BACK_NONE;
+ // an identifier or bareword
+ if (state == SCE_PL_IDENTIFIER) {
+ if ((!isWordStart(chNext) && chNext != '\'')
+ || (chNext == '.' && chNext2 == '.')) {
+ // We need that if length of word == 1!
+ // This test is copied from the SCE_PL_WORD handler.
+ styler.ColourTo(i, SCE_PL_IDENTIFIER);
+ state = SCE_PL_DEFAULT;
+ }
+ // a keyword
+ } else if (state == SCE_PL_WORD) {
+ i = kw - 1;
+ if (ch == '_' && chNext == '_' &&
+ (isMatch(styler, lengthDoc, styler.GetStartSegment(), "__DATA__")
+ || isMatch(styler, lengthDoc, styler.GetStartSegment(), "__END__"))) {
+ styler.ColourTo(i, SCE_PL_DATASECTION);
+ state = SCE_PL_DATASECTION;
+ } else {
+ styler.ColourTo(i, SCE_PL_WORD);
+ state = SCE_PL_DEFAULT;
+ backflag = BACK_KEYWORD;
+ backPos = i;
+ }
+ ch = styler.SafeGetCharAt(i);
+ chNext = styler.SafeGetCharAt(i + 1);
+ // a repetition operator 'x'
+ } else if (state == SCE_PL_OPERATOR) {
+ styler.ColourTo(i, SCE_PL_OPERATOR);
+ state = SCE_PL_DEFAULT;
+ // quote-like delimiter, skip one char if double-char delimiter
+ } else {
+ i = kw - 1;
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ } else if (ch == '#') {
+ state = SCE_PL_COMMENTLINE;
+ } else if (ch == '\"') {
+ state = SCE_PL_STRING;
+ Quote.New(1);
+ Quote.Open(ch);
+ backflag = BACK_NONE;
+ } else if (ch == '\'') {
+ if (chPrev == '&') {
+ // Archaic call
+ styler.ColourTo(i, state);
+ } else {
+ state = SCE_PL_CHARACTER;
+ Quote.New(1);
+ Quote.Open(ch);
+ }
+ backflag = BACK_NONE;
+ } else if (ch == '`') {
+ state = SCE_PL_BACKTICKS;
+ Quote.New(1);
+ Quote.Open(ch);
+ backflag = BACK_NONE;
+ } else if (ch == '$') {
+ if ((chNext == '{') || isspacechar(chNext)) {
+ styler.ColourTo(i, SCE_PL_SCALAR);
+ } else {
+ state = SCE_PL_SCALAR;
+ if ((chNext == '`' && chNext2 == '`')
+ || (chNext == ':' && chNext2 == ':')) {
+ i += 2;
+ ch = styler.SafeGetCharAt(i);
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else {
+ i++;
+ ch = chNext;
+ chNext = chNext2;
+ }
+ }
+ backflag = BACK_NONE;
+ } else if (ch == '@') {
+ if (!isascii(chNext) || isalpha(chNext) || chNext == '#' || chNext == '$'
+ || chNext == '_' || chNext == '+' || chNext == '-') {
+ state = SCE_PL_ARRAY;
+ } else if (chNext == ':' && chNext2 == ':') {
+ state = SCE_PL_ARRAY;
+ i += 2;
+ ch = styler.SafeGetCharAt(i);
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else if (chNext != '{' && chNext != '[') {
+ styler.ColourTo(i, SCE_PL_ARRAY);
+ } else {
+ styler.ColourTo(i, SCE_PL_ARRAY);
+ }
+ backflag = BACK_NONE;
+ } else if (ch == '%') {
+ if (!isascii(chNext) || isalpha(chNext) || chNext == '#' || chNext == '$'
+ || chNext == '_' || chNext == '!' || chNext == '^') {
+ state = SCE_PL_HASH;
+ i++;
+ ch = chNext;
+ chNext = chNext2;
+ } else if (chNext == ':' && chNext2 == ':') {
+ state = SCE_PL_HASH;
+ i += 2;
+ ch = styler.SafeGetCharAt(i);
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else if (chNext == '{') {
+ styler.ColourTo(i, SCE_PL_HASH);
+ } else {
+ styler.ColourTo(i, SCE_PL_OPERATOR);
+ }
+ backflag = BACK_NONE;
+ } else if (ch == '*') {
+ char strch[2];
+ strch[0] = chNext;
+ strch[1] = '\0';
+ if (chNext == ':' && chNext2 == ':') {
+ state = SCE_PL_SYMBOLTABLE;
+ i += 2;
+ ch = styler.SafeGetCharAt(i);
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else if (!isascii(chNext) || isalpha(chNext) || chNext == '_'
+ || NULL != strstr("^/|,\\\";#%^:?<>)[]", strch)) {
+ state = SCE_PL_SYMBOLTABLE;
+ i++;
+ ch = chNext;
+ chNext = chNext2;
+ } else if (chNext == '{') {
+ styler.ColourTo(i, SCE_PL_SYMBOLTABLE);
+ } else {
+ if (chNext == '*') { // exponentiation
+ i++;
+ ch = chNext;
+ chNext = chNext2;
+ }
+ styler.ColourTo(i, SCE_PL_OPERATOR);
+ }
+ backflag = BACK_NONE;
+ } else if (ch == '/' || (ch == '<' && chNext == '<')) {
+ // Explicit backward peeking to set a consistent preferRE for
+ // any slash found, so no longer need to track preferRE state.
+ // Find first previous significant lexed element and interpret.
+ // Test for HERE doc start '<<' shares this code, helps to
+ // determine if it should be an operator.
+ bool preferRE = false;
+ bool isHereDoc = (ch == '<');
+ bool hereDocSpace = false; // these are for corner case:
+ bool hereDocScalar = false; // SCALAR [whitespace] '<<'
+ unsigned int bk = (i > 0)? i - 1: 0;
+ char bkch;
+ styler.Flush();
+ if (styler.StyleAt(bk) == SCE_PL_DEFAULT)
+ hereDocSpace = true;
+ while ((bk > 0) && (styler.StyleAt(bk) == SCE_PL_DEFAULT ||
+ styler.StyleAt(bk) == SCE_PL_COMMENTLINE)) {
+ bk--;
+ }
+ if (bk == 0) {
+ // position 0 won't really be checked; rarely happens
+ // hard to fix due to an unsigned index i
+ preferRE = true;
+ } else {
+ int bkstyle = styler.StyleAt(bk);
+ bkch = styler.SafeGetCharAt(bk);
+ switch(bkstyle) {
+ case SCE_PL_OPERATOR:
+ preferRE = true;
+ if (bkch == ')' || bkch == ']') {
+ preferRE = false;
+ } else if (bkch == '}') {
+ // backtrack further, count balanced brace pairs
+ // if a brace pair found, see if it's a variable
+ int braceCount = 1;
+ while (--bk > 0) {
+ bkstyle = styler.StyleAt(bk);
+ if (bkstyle == SCE_PL_OPERATOR) {
+ bkch = styler.SafeGetCharAt(bk);
+ if (bkch == ';') { // early out
+ break;
+ } else if (bkch == '}') {
+ braceCount++;
+ } else if (bkch == '{') {
+ if (--braceCount == 0)
+ break;
+ }
+ }
+ }
+ if (bk == 0) {
+ // at beginning, true
+ } else if (braceCount == 0) {
+ // balanced { found, bk>0, skip more whitespace
+ if (styler.StyleAt(--bk) == SCE_PL_DEFAULT) {
+ while (bk > 0) {
+ bkstyle = styler.StyleAt(--bk);
+ if (bkstyle != SCE_PL_DEFAULT)
+ break;
+ }
+ }
+ bkstyle = styler.StyleAt(bk);
+ if (bkstyle == SCE_PL_SCALAR
+ || bkstyle == SCE_PL_ARRAY
+ || bkstyle == SCE_PL_HASH
+ || bkstyle == SCE_PL_SYMBOLTABLE
+ || bkstyle == SCE_PL_OPERATOR) {
+ preferRE = false;
+ }
+ }
+ }
+ break;
+ case SCE_PL_IDENTIFIER:
+ preferRE = true;
+ if (bkch == '>') { // inputsymbol
+ preferRE = false;
+ break;
+ }
+ // backtrack to find "->" or "::" before identifier
+ while (bk > 0 && styler.StyleAt(bk) == SCE_PL_IDENTIFIER) {
+ bk--;
+ }
+ while (bk > 0) {
+ bkstyle = styler.StyleAt(bk);
+ if (bkstyle == SCE_PL_DEFAULT ||
+ bkstyle == SCE_PL_COMMENTLINE) {
+ } else if (bkstyle == SCE_PL_OPERATOR) {
+ // gcc 3.2.3 bloats if more compact form used
+ bkch = styler.SafeGetCharAt(bk);
+ if (bkch == '>') { // "->"
+ if (styler.SafeGetCharAt(bk - 1) == '-') {
+ preferRE = false;
+ break;
+ }
+ } else if (bkch == ':') { // "::"
+ if (styler.SafeGetCharAt(bk - 1) == ':') {
+ preferRE = false;
+ break;
+ }
+ }
+ } else {// bare identifier, usually a function call but Perl
+ // optimizes them as pseudo-constants, then the next
+ // '/' will be a divide; favour divide over regex
+ // if there is a whitespace after the '/'
+ if (isspacechar(chNext)) {
+ preferRE = false;
+ }
+ break;
+ }
+ bk--;
+ }
+ break;
+ case SCE_PL_SCALAR: // for $var<< case
+ hereDocScalar = true;
+ break;
+ // other styles uses the default, preferRE=false
+ case SCE_PL_WORD:
+ case SCE_PL_POD:
+ case SCE_PL_POD_VERB:
+ case SCE_PL_HERE_Q:
+ case SCE_PL_HERE_QQ:
+ case SCE_PL_HERE_QX:
+ preferRE = true;
+ break;
+ }
+ }
+ if (isHereDoc) { // handle HERE doc
+ // if SCALAR whitespace '<<', *always* a HERE doc
+ if (preferRE || (hereDocSpace && hereDocScalar)) {
+ state = SCE_PL_HERE_DELIM;
+ HereDoc.State = 0;
+ } else { // << operator
+ i++;
+ ch = chNext;
+ chNext = chNext2;
+ styler.ColourTo(i, SCE_PL_OPERATOR);
+ }
+ } else { // handle regexp
+ if (preferRE) {
+ state = SCE_PL_REGEX;
+ Quote.New(1);
+ Quote.Open(ch);
+ } else { // / operator
+ styler.ColourTo(i, SCE_PL_OPERATOR);
+ }
+ }
+ backflag = BACK_NONE;
+ } else if (ch == '<') {
+ // looks forward for matching > on same line
+ unsigned int fw = i + 1;
+ while (fw < lengthDoc) {
+ char fwch = styler.SafeGetCharAt(fw);
+ if (fwch == ' ') {
+ if (styler.SafeGetCharAt(fw-1) != '\\' ||
+ styler.SafeGetCharAt(fw-2) != '\\')
+ break;
+ } else if (isEOLChar(fwch) || isspacechar(fwch)) {
+ break;
+ } else if (fwch == '>') {
+ if ((fw - i) == 2 && // '<=>' case
+ styler.SafeGetCharAt(fw-1) == '=') {
+ styler.ColourTo(fw, SCE_PL_OPERATOR);
+ } else {
+ styler.ColourTo(fw, SCE_PL_IDENTIFIER);
+ }
+ i = fw;
+ ch = fwch;
+ chNext = styler.SafeGetCharAt(i+1);
+ }
+ fw++;
+ }
+ styler.ColourTo(i, SCE_PL_OPERATOR);
+ backflag = BACK_NONE;
+ } else if (ch == '=' // POD
+ && isalpha(chNext)
+ && (isEOLChar(chPrev))) {
+ state = SCE_PL_POD;
+ backflag = BACK_NONE;
+ //sookedpos = 0;
+ //sooked[sookedpos] = '\0';
+ } else if (ch == '-' // file test operators
+ && isSingleCharOp(chNext)
+ && !isalnum((chNext2 = styler.SafeGetCharAt(i+2)))) {
+ styler.ColourTo(i + 1, SCE_PL_WORD);
+ state = SCE_PL_DEFAULT;
+ i++;
+ ch = chNext;
+ chNext = chNext2;
+ backflag = BACK_NONE;
+ } else if (isPerlOperator(ch)) {
+ if (ch == '.' && chNext == '.') { // .. and ...
+ i++;
+ if (chNext2 == '.') { i++; }
+ state = SCE_PL_DEFAULT;
+ ch = styler.SafeGetCharAt(i);
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ styler.ColourTo(i, SCE_PL_OPERATOR);
+ backflag = BACK_OPERATOR;
+ backPos = i;
+ } else {
+ // keep colouring defaults to make restart easier
+ styler.ColourTo(i, SCE_PL_DEFAULT);
+ }
+ } else if (state == SCE_PL_NUMBER) {
+ if (ch == '.') {
+ if (chNext == '.') {
+ // double dot is always an operator
+ goto numAtEnd;
+ } else if (numState <= PERLNUM_FLOAT) {
+ // non-decimal number or float exponent, consume next dot
+ styler.ColourTo(i - 1, SCE_PL_NUMBER);
+ styler.ColourTo(i, SCE_PL_OPERATOR);
+ state = SCE_PL_DEFAULT;
+ } else { // decimal or vectors allows dots
+ dotCount++;
+ if (numState == PERLNUM_DECIMAL) {
+ if (dotCount > 1) {
+ if (isdigit(chNext)) { // really a vector
+ numState = PERLNUM_VECTOR;
+ } else // number then dot
+ goto numAtEnd;
+ }
+ } else { // vectors
+ if (!isdigit(chNext)) // vector then dot
+ goto numAtEnd;
+ }
+ }
+ } else if (ch == '_' && numState == PERLNUM_DECIMAL) {
+ if (!isdigit(chNext)) {
+ goto numAtEnd;
+ }
+ } else if (!isascii(ch) || isalnum(ch)) {
+ if (numState == PERLNUM_VECTOR || numState == PERLNUM_V_VECTOR) {
+ if (!isascii(ch) || isalpha(ch)) {
+ if (dotCount == 0) { // change to word
+ state = SCE_PL_IDENTIFIER;
+ } else { // vector then word
+ goto numAtEnd;
+ }
+ }
+ } else if (numState == PERLNUM_DECIMAL) {
+ if (ch == 'E' || ch == 'e') { // exponent
+ numState = PERLNUM_FLOAT;
+ if (chNext == '+' || chNext == '-') {
+ i++;
+ ch = chNext;
+ chNext = chNext2;
+ }
+ } else if (!isascii(ch) || !isdigit(ch)) { // number then word
+ goto numAtEnd;
+ }
+ } else if (numState == PERLNUM_FLOAT) {
+ if (!isdigit(ch)) { // float then word
+ goto numAtEnd;
+ }
+ } else if (numState == PERLNUM_OCTAL) {
+ if (!isdigit(ch))
+ goto numAtEnd;
+ else if (ch > '7')
+ numState = PERLNUM_BAD;
+ } else if (numState == PERLNUM_BINARY) {
+ if (!isdigit(ch))
+ goto numAtEnd;
+ else if (ch > '1')
+ numState = PERLNUM_BAD;
+ } else if (numState == PERLNUM_HEX) {
+ int ch2 = toupper(ch);
+ if (!isdigit(ch) && !(ch2 >= 'A' && ch2 <= 'F'))
+ goto numAtEnd;
+ } else {//(numState == PERLNUM_BAD) {
+ if (!isdigit(ch))
+ goto numAtEnd;
+ }
+ } else {
+ // complete current number or vector
+ numAtEnd:
+ styler.ColourTo(i - 1, actualNumStyle(numState));
+ state = SCE_PL_DEFAULT;
+ goto restartLexer;
+ }
+ } else if (state == SCE_PL_IDENTIFIER) {
+ if (!isWordStart(chNext) && chNext != '\'') {
+ styler.ColourTo(i, SCE_PL_IDENTIFIER);
+ state = SCE_PL_DEFAULT;
+ ch = ' ';
+ }
+ } else {
+ if (state == SCE_PL_COMMENTLINE) {
+ if (isEOLChar(ch)) {
+ styler.ColourTo(i - 1, state);
+ state = SCE_PL_DEFAULT;
+ goto restartLexer;
+ } else if (isEOLChar(chNext)) {
+ styler.ColourTo(i, state);
+ state = SCE_PL_DEFAULT;
+ }
+ } else if (state == SCE_PL_HERE_DELIM) {
+ //
+ // From perldata.pod:
+ // ------------------
+ // A line-oriented form of quoting is based on the shell ``here-doc''
+ // syntax.
+ // Following a << you specify a string to terminate the quoted material,
+ // and all lines following the current line down to the terminating
+ // string are the value of the item.
+ // The terminating string may be either an identifier (a word),
+ // or some quoted text.
+ // If quoted, the type of quotes you use determines the treatment of
+ // the text, just as in regular quoting.
+ // An unquoted identifier works like double quotes.
+ // There must be no space between the << and the identifier.
+ // (If you put a space it will be treated as a null identifier,
+ // which is valid, and matches the first empty line.)
+ // (This is deprecated, -w warns of this syntax)
+ // The terminating string must appear by itself (unquoted and with no
+ // surrounding whitespace) on the terminating line.
+ //
+ // From Bash info:
+ // ---------------
+ // Specifier format is: <<[-]WORD
+ // Optional '-' is for removal of leading tabs from here-doc.
+ // Whitespace acceptable after <<[-] operator.
+ //
+ if (HereDoc.State == 0) { // '<<' encountered
+ bool gotspace = false;
+ unsigned int oldi = i;
+ if (chNext == ' ' || chNext == '\t') {
+ // skip whitespace; legal for quoted delimiters
+ gotspace = true;
+ do {
+ i++;
+ chNext = styler.SafeGetCharAt(i + 1);
+ } while ((i + 1 < lengthDoc) && (chNext == ' ' || chNext == '\t'));
+ chNext2 = styler.SafeGetCharAt(i + 2);
+ }
+ HereDoc.State = 1;
+ HereDoc.Quote = chNext;
+ HereDoc.Quoted = false;
+ HereDoc.DelimiterLength = 0;
+ HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0';
+ if (chNext == '\'' || chNext == '"' || chNext == '`') {
+ // a quoted here-doc delimiter
+ i++;
+ ch = chNext;
+ chNext = chNext2;
+ HereDoc.Quoted = true;
+ } else if (isspacechar(chNext) || isdigit(chNext) || chNext == '\\'
+ || chNext == '=' || chNext == '$' || chNext == '@'
+ || ((isalpha(chNext) || chNext == '_') && gotspace)) {
+ // left shift << or <<= operator cases
+ // restore position if operator
+ i = oldi;
+ styler.ColourTo(i, SCE_PL_OPERATOR);
+ state = SCE_PL_DEFAULT;
+ HereDoc.State = 0;
+ goto restartLexer;
+ } else {
+ // an unquoted here-doc delimiter, no special handling
+ // (cannot be prefixed by spaces/tabs), or
+ // symbols terminates; deprecated zero-length delimiter
+ }
+
+ } else if (HereDoc.State == 1) { // collect the delimiter
+ backflag = BACK_NONE;
+ if (HereDoc.Quoted) { // a quoted here-doc delimiter
+ if (ch == HereDoc.Quote) { // closing quote => end of delimiter
+ styler.ColourTo(i, state);
+ state = SCE_PL_DEFAULT;
+ } else {
+ if (ch == '\\' && chNext == HereDoc.Quote) { // escaped quote
+ i++;
+ ch = chNext;
+ chNext = chNext2;
+ }
+ HereDoc.Delimiter[HereDoc.DelimiterLength++] = ch;
+ HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0';
+ }
+ } else { // an unquoted here-doc delimiter
+ if (isalnum(ch) || ch == '_') {
+ HereDoc.Delimiter[HereDoc.DelimiterLength++] = ch;
+ HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0';
+ } else {
+ styler.ColourTo(i - 1, state);
+ state = SCE_PL_DEFAULT;
+ goto restartLexer;
+ }
+ }
+ if (HereDoc.DelimiterLength >= HERE_DELIM_MAX - 1) {
+ styler.ColourTo(i - 1, state);
+ state = SCE_PL_ERROR;
+ goto restartLexer;
+ }
+ }
+ } else if (HereDoc.State == 2) {
+ // state == SCE_PL_HERE_Q || state == SCE_PL_HERE_QQ || state == SCE_PL_HERE_QX
+ if (isEOLChar(chPrev) && isMatch(styler, lengthDoc, i, HereDoc.Delimiter)) {
+ i += HereDoc.DelimiterLength;
+ chPrev = styler.SafeGetCharAt(i - 1);
+ ch = styler.SafeGetCharAt(i);
+ if (isEOLChar(ch)) {
+ styler.ColourTo(i - 1, state);
+ state = SCE_PL_DEFAULT;
+ backflag = BACK_NONE;
+ HereDoc.State = 0;
+ goto restartLexer;
+ }
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ } else if (state == SCE_PL_POD
+ || state == SCE_PL_POD_VERB) {
+ if (isEOLChar(chPrev)) {
+ if (ch == ' ' || ch == '\t') {
+ styler.ColourTo(i - 1, state);
+ state = SCE_PL_POD_VERB;
+ } else {
+ styler.ColourTo(i - 1, state);
+ state = SCE_PL_POD;
+ if (ch == '=') {
+ if (isMatch(styler, lengthDoc, i, "=cut")) {
+ styler.ColourTo(i - 1 + 4, state);
+ i += 4;
+ state = SCE_PL_DEFAULT;
+ ch = styler.SafeGetCharAt(i);
+ //chNext = styler.SafeGetCharAt(i + 1);
+ goto restartLexer;
+ }
+ }
+ }
+ }
+ } else if (state == SCE_PL_SCALAR // variable names
+ || state == SCE_PL_ARRAY
+ || state == SCE_PL_HASH
+ || state == SCE_PL_SYMBOLTABLE) {
+ if (ch == ':' && chNext == ':') { // skip ::
+ i++;
+ ch = chNext;
+ chNext = chNext2;
+ }
+ else if (isEndVar(ch)) {
+ if (i == (styler.GetStartSegment() + 1)) {
+ // Special variable: $(, $_ etc.
+ styler.ColourTo(i, state);
+ state = SCE_PL_DEFAULT;
+ } else {
+ styler.ColourTo(i - 1, state);
+ state = SCE_PL_DEFAULT;
+ goto restartLexer;
+ }
+ }
+ } else if (state == SCE_PL_REGEX
+ || state == SCE_PL_STRING_QR
+ ) {
+ if (!Quote.Up && !isspacechar(ch)) {
+ Quote.Open(ch);
+ } else if (ch == '\\' && Quote.Up != '\\') {
+ // SG: Is it save to skip *every* escaped char?
+ i++;
+ ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else {
+ if (ch == Quote.Down /*&& chPrev != '\\'*/) {
+ Quote.Count--;
+ if (Quote.Count == 0) {
+ Quote.Rep--;
+ if (Quote.Up == Quote.Down) {
+ Quote.Count++;
+ }
+ }
+ if (!isalpha(chNext)) {
+ if (Quote.Rep <= 0) {
+ styler.ColourTo(i, state);
+ state = SCE_PL_DEFAULT;
+ ch = ' ';
+ }
+ }
+ } else if (ch == Quote.Up /*&& chPrev != '\\'*/) {
+ Quote.Count++;
+ } else if (!isascii(chNext) || !isalpha(chNext)) {
+ if (Quote.Rep <= 0) {
+ styler.ColourTo(i, state);
+ state = SCE_PL_DEFAULT;
+ ch = ' ';
+ }
+ }
+ }
+ } else if (state == SCE_PL_REGSUBST) {
+ if (!Quote.Up && !isspacechar(ch)) {
+ Quote.Open(ch);
+ } else if (ch == '\\' && Quote.Up != '\\') {
+ // SG: Is it save to skip *every* escaped char?
+ i++;
+ ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else {
+ if (Quote.Count == 0 && Quote.Rep == 1) {
+ /* We matched something like s(...) or tr{...}
+ * and are looking for the next matcher characters,
+ * which could be either bracketed ({...}) or non-bracketed
+ * (/.../).
+ *
+ * Number-signs are problematic. If they occur after
+ * the close of the first part, treat them like
+ * a Quote.Up char, even if they actually start comments.
+ *
+ * If we find an alnum, we end the regsubst, and punt.
+ *
+ * Eric Promislow ericp@activestate.com Aug 9,2000
+ */
+ if (isspacechar(ch)) {
+ // Keep going
+ }
+ else if (!isascii(ch) || isalnum(ch)) {
+ styler.ColourTo(i, state);
+ state = SCE_PL_DEFAULT;
+ ch = ' ';
+ } else {
+ Quote.Open(ch);
+ }
+ } else if (ch == Quote.Down /*&& chPrev != '\\'*/) {
+ Quote.Count--;
+ if (Quote.Count == 0) {
+ Quote.Rep--;
+ }
+ if (!isascii(chNext) || !isalpha(chNext)) {
+ if (Quote.Rep <= 0) {
+ styler.ColourTo(i, state);
+ state = SCE_PL_DEFAULT;
+ ch = ' ';
+ }
+ }
+ if (Quote.Up == Quote.Down) {
+ Quote.Count++;
+ }
+ } else if (ch == Quote.Up /*&& chPrev != '\\'*/) {
+ Quote.Count++;
+ } else if (!isascii(chNext) || !isalpha(chNext)) {
+ if (Quote.Rep <= 0) {
+ styler.ColourTo(i, state);
+ state = SCE_PL_DEFAULT;
+ ch = ' ';
+ }
+ }
+ }
+ } else if (state == SCE_PL_STRING_Q
+ || state == SCE_PL_STRING_QQ
+ || state == SCE_PL_STRING_QX
+ || state == SCE_PL_STRING_QW
+ || state == SCE_PL_STRING
+ || state == SCE_PL_CHARACTER
+ || state == SCE_PL_BACKTICKS
+ ) {
+ if (!Quote.Down && !isspacechar(ch)) {
+ Quote.Open(ch);
+ } else if (ch == '\\' && Quote.Up != '\\') {
+ i++;
+ ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else if (ch == Quote.Down) {
+ Quote.Count--;
+ if (Quote.Count == 0) {
+ Quote.Rep--;
+ if (Quote.Rep <= 0) {
+ styler.ColourTo(i, state);
+ state = SCE_PL_DEFAULT;
+ ch = ' ';
+ }
+ if (Quote.Up == Quote.Down) {
+ Quote.Count++;
+ }
+ }
+ } else if (ch == Quote.Up) {
+ Quote.Count++;
+ }
+ }
+ }
+ if (state == SCE_PL_ERROR) {
+ break;
+ }
+ chPrev = ch;
+ }
+ styler.ColourTo(lengthDoc - 1, state);
+}
+
+static bool IsCommentLine(int line, Accessor &styler) {
+ int pos = styler.LineStart(line);
+ int eol_pos = styler.LineStart(line + 1) - 1;
+ for (int i = pos; i < eol_pos; i++) {
+ char ch = styler[i];
+ int style = styler.StyleAt(i);
+ if (ch == '#' && style == SCE_PL_COMMENTLINE)
+ return true;
+ else if (ch != ' ' && ch != '\t')
+ return false;
+ }
+ return false;
+}
+
+static void FoldPerlDoc(unsigned int startPos, int length, int, WordList *[],
+ Accessor &styler) {
+ bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ // Custom folding of POD and packages
+ bool foldPOD = styler.GetPropertyInt("fold.perl.pod", 1) != 0;
+ bool foldPackage = styler.GetPropertyInt("fold.perl.package", 1) != 0;
+ unsigned int endPos = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelPrev = SC_FOLDLEVELBASE;
+ if (lineCurrent > 0)
+ levelPrev = styler.LevelAt(lineCurrent - 1) >> 16;
+ int levelCurrent = levelPrev;
+ char chNext = styler[startPos];
+ char chPrev = styler.SafeGetCharAt(startPos - 1);
+ int styleNext = styler.StyleAt(startPos);
+ // Used at end of line to determine if the line was a package definition
+ bool isPackageLine = false;
+ bool isPodHeading = false;
+ for (unsigned int i = startPos; i < endPos; 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');
+ bool atLineStart = isEOLChar(chPrev) || i == 0;
+ // Comment folding
+ if (foldComment && atEOL && IsCommentLine(lineCurrent, styler))
+ {
+ if (!IsCommentLine(lineCurrent - 1, styler)
+ && IsCommentLine(lineCurrent + 1, styler))
+ levelCurrent++;
+ else if (IsCommentLine(lineCurrent - 1, styler)
+ && !IsCommentLine(lineCurrent+1, styler))
+ levelCurrent--;
+ }
+ if (style == SCE_C_OPERATOR) {
+ if (ch == '{') {
+ levelCurrent++;
+ } else if (ch == '}') {
+ levelCurrent--;
+ }
+ }
+ // Custom POD folding
+ if (foldPOD && atLineStart) {
+ int stylePrevCh = (i) ? styler.StyleAt(i - 1):SCE_PL_DEFAULT;
+ if (style == SCE_PL_POD) {
+ if (stylePrevCh != SCE_PL_POD && stylePrevCh != SCE_PL_POD_VERB)
+ levelCurrent++;
+ else if (styler.Match(i, "=cut"))
+ levelCurrent--;
+ else if (styler.Match(i, "=head"))
+ isPodHeading = true;
+ } else if (style == SCE_PL_DATASECTION) {
+ if (ch == '=' && isalpha(chNext) && levelCurrent == SC_FOLDLEVELBASE)
+ levelCurrent++;
+ else if (styler.Match(i, "=cut") && levelCurrent > SC_FOLDLEVELBASE)
+ levelCurrent--;
+ else if (styler.Match(i, "=head"))
+ isPodHeading = true;
+ // if package used or unclosed brace, level > SC_FOLDLEVELBASE!
+ // reset needed as level test is vs. SC_FOLDLEVELBASE
+ else if (styler.Match(i, "__END__"))
+ levelCurrent = SC_FOLDLEVELBASE;
+ }
+ }
+ // Custom package folding
+ if (foldPackage && atLineStart) {
+ if (style == SCE_PL_WORD && styler.Match(i, "package")) {
+ isPackageLine = true;
+ }
+ }
+
+ if (atEOL) {
+ int lev = levelPrev;
+ if (isPodHeading) {
+ lev = levelPrev - 1;
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ isPodHeading = false;
+ }
+ // Check if line was a package declaration
+ // because packages need "special" treatment
+ if (isPackageLine) {
+ lev = SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG;
+ levelCurrent = SC_FOLDLEVELBASE + 1;
+ isPackageLine = false;
+ }
+ lev |= levelCurrent << 16;
+ if (visibleChars == 0 && foldCompact)
+ 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++;
+ chPrev = ch;
+ }
+ // 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 perlWordListDesc[] = {
+ "Keywords",
+ 0
+};
+
+LexerModule lmPerl(SCLEX_PERL, ColourisePerlDoc, "perl", FoldPerlDoc, perlWordListDesc);
+
diff --git a/src/LexPython.cpp b/src/LexPython.cpp
new file mode 100755
index 0000000..1bdebf6
--- /dev/null
+++ b/src/LexPython.cpp
@@ -0,0 +1,449 @@
+// Scintilla source code edit control
+/** @file LexPython.cxx
+ ** Lexer for Python.
+ **/
+// Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+enum kwType { kwOther, kwClass, kwDef, kwImport };
+
+static bool IsPyComment(Accessor &styler, int pos, int len) {
+ return len > 0 && styler[pos] == '#';
+}
+
+static bool IsPyStringStart(int ch, int chNext, int chNext2) {
+ if (ch == '\'' || ch == '"')
+ return true;
+ if (ch == 'u' || ch == 'U') {
+ if (chNext == '"' || chNext == '\'')
+ return true;
+ if ((chNext == 'r' || chNext == 'R') && (chNext2 == '"' || chNext2 == '\''))
+ return true;
+ }
+ if ((ch == 'r' || ch == 'R') && (chNext == '"' || chNext == '\''))
+ return true;
+
+ return false;
+}
+
+/* Return the state to use for the string starting at i; *nextIndex will be set to the first index following the quote(s) */
+static int GetPyStringState(Accessor &styler, int i, unsigned int *nextIndex) {
+ char ch = styler.SafeGetCharAt(i);
+ char chNext = styler.SafeGetCharAt(i + 1);
+
+ // Advance beyond r, u, or ur prefix, but bail if there are any unexpected chars
+ if (ch == 'r' || ch == 'R') {
+ i++;
+ ch = styler.SafeGetCharAt(i);
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else if (ch == 'u' || ch == 'U') {
+ if (chNext == 'r' || chNext == 'R')
+ i += 2;
+ else
+ i += 1;
+ ch = styler.SafeGetCharAt(i);
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+
+ if (ch != '"' && ch != '\'') {
+ *nextIndex = i + 1;
+ return SCE_P_DEFAULT;
+ }
+
+ if (ch == chNext && ch == styler.SafeGetCharAt(i + 2)) {
+ *nextIndex = i + 3;
+
+ if (ch == '"')
+ return SCE_P_TRIPLEDOUBLE;
+ else
+ return SCE_P_TRIPLE;
+ } else {
+ *nextIndex = i + 1;
+
+ if (ch == '"')
+ return SCE_P_STRING;
+ else
+ return SCE_P_CHARACTER;
+ }
+}
+
+static inline bool IsAWordChar(int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_');
+}
+
+static inline bool IsAWordStart(int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '_');
+}
+
+static void ColourisePyDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler) {
+
+ int endPos = startPos + length;
+
+ // Backtrack to previous line in case need to fix its tab whinging
+ int lineCurrent = styler.GetLine(startPos);
+ if (startPos > 0) {
+ if (lineCurrent > 0) {
+ lineCurrent--;
+ startPos = styler.LineStart(lineCurrent);
+ if (startPos == 0)
+ initStyle = SCE_P_DEFAULT;
+ else
+ initStyle = styler.StyleAt(startPos - 1);
+ }
+ }
+
+ WordList &keywords = *keywordlists[0];
+ WordList &keywords2 = *keywordlists[1];
+
+ const int whingeLevel = styler.GetPropertyInt("tab.timmy.whinge.level");
+
+ initStyle = initStyle & 31;
+ if (initStyle == SCE_P_STRINGEOL) {
+ initStyle = SCE_P_DEFAULT;
+ }
+
+ kwType kwLast = kwOther;
+ int spaceFlags = 0;
+ styler.IndentAmount(lineCurrent, &spaceFlags, IsPyComment);
+ bool hexadecimal = false;
+
+ // Python uses a different mask because bad indentation is marked by oring with 32
+ StyleContext sc(startPos, endPos - startPos, initStyle, styler, 0x7f);
+
+ for (; sc.More(); sc.Forward()) {
+
+ if (sc.atLineStart) {
+ const char chBad = static_cast<char>(64);
+ const char chGood = static_cast<char>(0);
+ char chFlags = chGood;
+ if (whingeLevel == 1) {
+ chFlags = (spaceFlags & wsInconsistent) ? chBad : chGood;
+ } else if (whingeLevel == 2) {
+ chFlags = (spaceFlags & wsSpaceTab) ? chBad : chGood;
+ } else if (whingeLevel == 3) {
+ chFlags = (spaceFlags & wsSpace) ? chBad : chGood;
+ } else if (whingeLevel == 4) {
+ chFlags = (spaceFlags & wsTab) ? chBad : chGood;
+ }
+ sc.SetState(sc.state);
+ styler.SetFlags(chFlags, static_cast<char>(sc.state));
+ }
+
+ if (sc.atLineEnd) {
+ if ((sc.state == SCE_P_DEFAULT) ||
+ (sc.state == SCE_P_TRIPLE) ||
+ (sc.state == SCE_P_TRIPLEDOUBLE)) {
+ // Perform colourisation of white space and triple quoted strings at end of each line to allow
+ // tab marking to work inside white space and triple quoted strings
+ sc.SetState(sc.state);
+ }
+ lineCurrent++;
+ styler.IndentAmount(lineCurrent, &spaceFlags, IsPyComment);
+ if ((sc.state == SCE_P_STRING) || (sc.state == SCE_P_CHARACTER)) {
+ sc.ChangeState(SCE_P_STRINGEOL);
+ sc.ForwardSetState(SCE_P_DEFAULT);
+ }
+ if (!sc.More())
+ break;
+ }
+
+ bool needEOLCheck = false;
+
+ // Check for a state end
+ if (sc.state == SCE_P_OPERATOR) {
+ kwLast = kwOther;
+ sc.SetState(SCE_P_DEFAULT);
+ } else if (sc.state == SCE_P_NUMBER) {
+ if (!IsAWordChar(sc.ch) &&
+ !(!hexadecimal && ((sc.ch == '+' || sc.ch == '-') && (sc.chPrev == 'e' || sc.chPrev == 'E')))) {
+ sc.SetState(SCE_P_DEFAULT);
+ }
+ } else if (sc.state == SCE_P_IDENTIFIER) {
+ if ((sc.ch == '.') || (!IsAWordChar(sc.ch))) {
+ char s[100];
+ sc.GetCurrent(s, sizeof(s));
+ int style = SCE_P_IDENTIFIER;
+ if ((kwLast == kwImport) && (strcmp(s, "as") == 0)) {
+ style = SCE_P_WORD;
+ } else if (keywords.InList(s)) {
+ style = SCE_P_WORD;
+ } else if (kwLast == kwClass) {
+ style = SCE_P_CLASSNAME;
+ } else if (kwLast == kwDef) {
+ style = SCE_P_DEFNAME;
+ } else if (keywords2.InList(s)) {
+ style = SCE_P_WORD2;
+ }
+ sc.ChangeState(style);
+ sc.SetState(SCE_P_DEFAULT);
+ if (style == SCE_P_WORD) {
+ if (0 == strcmp(s, "class"))
+ kwLast = kwClass;
+ else if (0 == strcmp(s, "def"))
+ kwLast = kwDef;
+ else if (0 == strcmp(s, "import"))
+ kwLast = kwImport;
+ else
+ kwLast = kwOther;
+ } else {
+ kwLast = kwOther;
+ }
+ }
+ } else if ((sc.state == SCE_P_COMMENTLINE) || (sc.state == SCE_P_COMMENTBLOCK)) {
+ if (sc.ch == '\r' || sc.ch == '\n') {
+ sc.SetState(SCE_P_DEFAULT);
+ }
+ } else if (sc.state == SCE_P_DECORATOR) {
+ if (sc.ch == '\r' || sc.ch == '\n') {
+ sc.SetState(SCE_P_DEFAULT);
+ } else if (sc.ch == '#') {
+ sc.SetState((sc.chNext == '#') ? SCE_P_COMMENTBLOCK : SCE_P_COMMENTLINE);
+ }
+ } else if ((sc.state == SCE_P_STRING) || (sc.state == SCE_P_CHARACTER)) {
+ if (sc.ch == '\\') {
+ if ((sc.chNext == '\r') && (sc.GetRelative(2) == '\n')) {
+ sc.Forward();
+ }
+ sc.Forward();
+ } else if ((sc.state == SCE_P_STRING) && (sc.ch == '\"')) {
+ sc.ForwardSetState(SCE_P_DEFAULT);
+ needEOLCheck = true;
+ } else if ((sc.state == SCE_P_CHARACTER) && (sc.ch == '\'')) {
+ sc.ForwardSetState(SCE_P_DEFAULT);
+ needEOLCheck = true;
+ }
+ } else if (sc.state == SCE_P_TRIPLE) {
+ if (sc.ch == '\\') {
+ sc.Forward();
+ } else if (sc.Match("\'\'\'")) {
+ sc.Forward();
+ sc.Forward();
+ sc.ForwardSetState(SCE_P_DEFAULT);
+ needEOLCheck = true;
+ }
+ } else if (sc.state == SCE_P_TRIPLEDOUBLE) {
+ if (sc.ch == '\\') {
+ sc.Forward();
+ } else if (sc.Match("\"\"\"")) {
+ sc.Forward();
+ sc.Forward();
+ sc.ForwardSetState(SCE_P_DEFAULT);
+ needEOLCheck = true;
+ }
+ }
+
+ // State exit code may have moved on to end of line
+ if (needEOLCheck && sc.atLineEnd) {
+ lineCurrent++;
+ styler.IndentAmount(lineCurrent, &spaceFlags, IsPyComment);
+ if (!sc.More())
+ break;
+ }
+
+ // Check for a new state starting character
+ if (sc.state == SCE_P_DEFAULT) {
+ if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
+ if (sc.ch == '0' && (sc.chNext == 'x' || sc.chNext == 'X')) {
+ hexadecimal = true;
+ } else {
+ hexadecimal = false;
+ }
+ sc.SetState(SCE_P_NUMBER);
+ } else if (isascii(sc.ch) && isoperator(static_cast<char>(sc.ch)) || sc.ch == '`') {
+ sc.SetState(SCE_P_OPERATOR);
+ } else if (sc.ch == '#') {
+ sc.SetState(sc.chNext == '#' ? SCE_P_COMMENTBLOCK : SCE_P_COMMENTLINE);
+ } else if (sc.ch == '@') {
+ sc.SetState(SCE_P_DECORATOR);
+ } else if (IsPyStringStart(sc.ch, sc.chNext, sc.GetRelative(2))) {
+ unsigned int nextIndex = 0;
+ sc.SetState(GetPyStringState(styler, sc.currentPos, &nextIndex));
+ while (nextIndex > (sc.currentPos + 1) && sc.More()) {
+ sc.Forward();
+ }
+ } else if (IsAWordStart(sc.ch)) {
+ sc.SetState(SCE_P_IDENTIFIER);
+ }
+ }
+ }
+ sc.Complete();
+}
+
+static bool IsCommentLine(int line, Accessor &styler) {
+ int pos = styler.LineStart(line);
+ int eol_pos = styler.LineStart(line + 1) - 1;
+ for (int i = pos; i < eol_pos; i++) {
+ char ch = styler[i];
+ if (ch == '#')
+ return true;
+ else if (ch != ' ' && ch != '\t')
+ return false;
+ }
+ return false;
+}
+
+static bool IsQuoteLine(int line, Accessor &styler) {
+ int style = styler.StyleAt(styler.LineStart(line)) & 31;
+ return ((style == SCE_P_TRIPLE) || (style == SCE_P_TRIPLEDOUBLE));
+}
+
+
+static void FoldPyDoc(unsigned int startPos, int length, int /*initStyle - unused*/,
+ WordList *[], Accessor &styler) {
+ const int maxPos = startPos + length;
+ const int maxLines = styler.GetLine(maxPos - 1); // Requested last line
+ const int docLines = styler.GetLine(styler.Length() - 1); // Available last line
+ const bool foldComment = styler.GetPropertyInt("fold.comment.python") != 0;
+ const bool foldQuotes = styler.GetPropertyInt("fold.quotes.python") != 0;
+
+ // Backtrack to previous non-blank line so we can determine indent level
+ // for any white space lines (needed esp. within triple quoted strings)
+ // and so we can fix any preceding fold level (which is why we go back
+ // at least one line in all cases)
+ int spaceFlags = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, NULL);
+ while (lineCurrent > 0) {
+ lineCurrent--;
+ indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, NULL);
+ if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG) &&
+ (!IsCommentLine(lineCurrent, styler)) &&
+ (!IsQuoteLine(lineCurrent, styler)))
+ break;
+ }
+ int indentCurrentLevel = indentCurrent & SC_FOLDLEVELNUMBERMASK;
+
+ // Set up initial loop state
+ startPos = styler.LineStart(lineCurrent);
+ int prev_state = SCE_P_DEFAULT & 31;
+ if (lineCurrent >= 1)
+ prev_state = styler.StyleAt(startPos - 1) & 31;
+ int prevQuote = foldQuotes && ((prev_state == SCE_P_TRIPLE) || (prev_state == SCE_P_TRIPLEDOUBLE));
+ int prevComment = 0;
+ if (lineCurrent >= 1)
+ prevComment = foldComment && IsCommentLine(lineCurrent - 1, styler);
+
+ // Process all characters to end of requested range or end of any triple quote
+ // or comment that hangs over the end of the range. Cap processing in all cases
+ // to end of document (in case of unclosed quote or comment at end).
+ while ((lineCurrent <= docLines) && ((lineCurrent <= maxLines) || prevQuote || prevComment)) {
+
+ // Gather info
+ int lev = indentCurrent;
+ int lineNext = lineCurrent + 1;
+ int indentNext = indentCurrent;
+ int quote = false;
+ if (lineNext <= docLines) {
+ // Information about next line is only available if not at end of document
+ indentNext = styler.IndentAmount(lineNext, &spaceFlags, NULL);
+ int style = styler.StyleAt(styler.LineStart(lineNext)) & 31;
+ quote = foldQuotes && ((style == SCE_P_TRIPLE) || (style == SCE_P_TRIPLEDOUBLE));
+ }
+ const int quote_start = (quote && !prevQuote);
+ const int quote_continue = (quote && prevQuote);
+ const int comment = foldComment && IsCommentLine(lineCurrent, styler);
+ const int comment_start = (comment && !prevComment && (lineNext <= docLines) &&
+ IsCommentLine(lineNext, styler) && (lev > SC_FOLDLEVELBASE));
+ const int comment_continue = (comment && prevComment);
+ if ((!quote || !prevQuote) && !comment)
+ indentCurrentLevel = indentCurrent & SC_FOLDLEVELNUMBERMASK;
+ if (quote)
+ indentNext = indentCurrentLevel;
+ if (indentNext & SC_FOLDLEVELWHITEFLAG)
+ indentNext = SC_FOLDLEVELWHITEFLAG | indentCurrentLevel;
+
+ if (quote_start) {
+ // Place fold point at start of triple quoted string
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ } else if (quote_continue || prevQuote) {
+ // Add level to rest of lines in the string
+ lev = lev + 1;
+ } else if (comment_start) {
+ // Place fold point at start of a block of comments
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ } else if (comment_continue) {
+ // Add level to rest of lines in the block
+ lev = lev + 1;
+ }
+
+ // Skip past any blank lines for next indent level info; we skip also
+ // comments (all comments, not just those starting in column 0)
+ // which effectively folds them into surrounding code rather
+ // than screwing up folding.
+
+ while (!quote &&
+ (lineNext < docLines) &&
+ ((indentNext & SC_FOLDLEVELWHITEFLAG) ||
+ (lineNext <= docLines && IsCommentLine(lineNext, styler)))) {
+
+ lineNext++;
+ indentNext = styler.IndentAmount(lineNext, &spaceFlags, NULL);
+ }
+
+ const int levelAfterComments = indentNext & SC_FOLDLEVELNUMBERMASK;
+ const int levelBeforeComments = Platform::Maximum(indentCurrentLevel,levelAfterComments);
+
+ // Now set all the indent levels on the lines we skipped
+ // Do this from end to start. Once we encounter one line
+ // which is indented more than the line after the end of
+ // the comment-block, use the level of the block before
+
+ int skipLine = lineNext;
+ int skipLevel = levelAfterComments;
+
+ while (--skipLine > lineCurrent) {
+ int skipLineIndent = styler.IndentAmount(skipLine, &spaceFlags, NULL);
+
+ if ((skipLineIndent & SC_FOLDLEVELNUMBERMASK) > levelAfterComments)
+ skipLevel = levelBeforeComments;
+
+ int whiteFlag = skipLineIndent & SC_FOLDLEVELWHITEFLAG;
+
+ styler.SetLevel(skipLine, skipLevel | whiteFlag);
+ }
+
+ // Set fold header on non-quote/non-comment line
+ if (!quote && !comment && !(indentCurrent & SC_FOLDLEVELWHITEFLAG) ) {
+ if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK))
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ }
+
+ // Keep track of triple quote and block comment state of previous line
+ prevQuote = quote;
+ prevComment = comment_start || comment_continue;
+
+ // Set fold level for this line and move to next line
+ styler.SetLevel(lineCurrent, lev);
+ indentCurrent = indentNext;
+ lineCurrent = lineNext;
+ }
+
+ // NOTE: Cannot set level of last line here because indentCurrent doesn't have
+ // header flag set; the loop above is crafted to take care of this case!
+ //styler.SetLevel(lineCurrent, indentCurrent);
+}
+
+static const char * const pythonWordListDesc[] = {
+ "Keywords",
+ "Highlighted identifiers",
+ 0
+};
+
+LexerModule lmPython(SCLEX_PYTHON, ColourisePyDoc, "python", FoldPyDoc,
+ pythonWordListDesc);
diff --git a/src/LexRebol.cpp b/src/LexRebol.cpp
new file mode 100644
index 0000000..f829c1e
--- /dev/null
+++ b/src/LexRebol.cpp
@@ -0,0 +1,319 @@
+// 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 <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+#include "StyleContext.h"
+
+
+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(unsigned int startPos, int 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];
+
+ int 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(unsigned int startPos, int length, int /* initStyle */, WordList *[],
+ Accessor &styler) {
+ unsigned int lengthDoc = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelPrev = styler.LevelAt(lineCurrent) & SC_FOLDLEVELNUMBERMASK;
+ int levelCurrent = levelPrev;
+ char chNext = styler[startPos];
+ int styleNext = styler.StyleAt(startPos);
+ for (unsigned int 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
+};
+
+LexerModule lmREBOL(SCLEX_REBOL, ColouriseRebolDoc, "rebol", FoldRebolDoc, rebolWordListDesc);
+
diff --git a/src/LexRuby.cpp b/src/LexRuby.cpp
new file mode 100755
index 0000000..f5ad020
--- /dev/null
+++ b/src/LexRuby.cpp
@@ -0,0 +1,1542 @@
+// Scintilla source code edit control
+/** @file LexRuby.cxx
+ ** Lexer for Ruby.
+ **/
+// Copyright 2001- by Clemens Wyss <wys@helbling.ch>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+#ifdef SCI_NAMESPACE
+using namespace Scintilla;
+#endif
+
+//XXX Identical to Perl, put in common area
+static inline bool isEOLChar(char ch) {
+ return (ch == '\r') || (ch == '\n');
+}
+
+#define isSafeASCII(ch) ((unsigned int)(ch) <= 127)
+// This one's redundant, but makes for more readable code
+#define isHighBitChar(ch) ((unsigned int)(ch) > 127)
+
+static inline bool isSafeAlpha(char ch) {
+ return (isSafeASCII(ch) && isalpha(ch)) || ch == '_';
+}
+
+static inline bool isSafeAlnum(char ch) {
+ return (isSafeASCII(ch) && isalnum(ch)) || ch == '_';
+}
+
+static inline bool isSafeAlnumOrHigh(char ch) {
+ return isHighBitChar(ch) || isalnum(ch) || ch == '_';
+}
+
+static inline bool isSafeDigit(char ch) {
+ return isSafeASCII(ch) && isdigit(ch);
+}
+
+static inline bool isSafeWordcharOrHigh(char ch) {
+ return isHighBitChar(ch) || iswordchar(ch);
+}
+
+static bool inline iswhitespace(char ch) {
+ return ch == ' ' || ch == '\t';
+}
+
+#define MAX_KEYWORD_LENGTH 200
+
+#define STYLE_MASK 63
+#define actual_style(style) (style & STYLE_MASK)
+
+static bool followsDot(unsigned int pos, Accessor &styler) {
+ styler.Flush();
+ for (; pos >= 1; --pos) {
+ int style = actual_style(styler.StyleAt(pos));
+ char ch;
+ switch (style) {
+ case SCE_RB_DEFAULT:
+ ch = styler[pos];
+ if (ch == ' ' || ch == '\t') {
+ //continue
+ } else {
+ return false;
+ }
+ break;
+
+ case SCE_RB_OPERATOR:
+ return styler[pos] == '.';
+
+ default:
+ return false;
+ }
+ }
+ return false;
+}
+
+// Forward declarations
+static bool keywordIsAmbiguous(const char *prevWord);
+static bool keywordDoStartsLoop(int pos,
+ Accessor &styler);
+static bool keywordIsModifier(const char *word,
+ int pos,
+ Accessor &styler);
+
+static int ClassifyWordRb(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler, char *prevWord) {
+ char s[100];
+ unsigned int i, j;
+ unsigned int lim = end - start + 1; // num chars to copy
+ if (lim >= MAX_KEYWORD_LENGTH) {
+ lim = MAX_KEYWORD_LENGTH - 1;
+ }
+ for (i = start, j = 0; j < lim; i++, j++) {
+ s[j] = styler[i];
+ }
+ s[j] = '\0';
+ int chAttr;
+ if (0 == strcmp(prevWord, "class"))
+ chAttr = SCE_RB_CLASSNAME;
+ else if (0 == strcmp(prevWord, "module"))
+ chAttr = SCE_RB_MODULE_NAME;
+ else if (0 == strcmp(prevWord, "def"))
+ chAttr = SCE_RB_DEFNAME;
+ else if (keywords.InList(s) && !followsDot(start - 1, styler)) {
+ if (keywordIsAmbiguous(s)
+ && keywordIsModifier(s, start, styler)) {
+
+ // Demoted keywords are colored as keywords,
+ // but do not affect changes in indentation.
+ //
+ // Consider the word 'if':
+ // 1. <<if test ...>> : normal
+ // 2. <<stmt if test>> : demoted
+ // 3. <<lhs = if ...>> : normal: start a new indent level
+ // 4. <<obj.if = 10>> : color as identifer, since it follows '.'
+
+ chAttr = SCE_RB_WORD_DEMOTED;
+ } else {
+ chAttr = SCE_RB_WORD;
+ }
+ } else
+ chAttr = SCE_RB_IDENTIFIER;
+ styler.ColourTo(end, chAttr);
+ if (chAttr == SCE_RB_WORD) {
+ strcpy(prevWord, s);
+ } else {
+ prevWord[0] = 0;
+ }
+ return chAttr;
+}
+
+
+//XXX Identical to Perl, put in common area
+static bool isMatch(Accessor &styler, int lengthDoc, int pos, const char *val) {
+ if ((pos + static_cast<int>(strlen(val))) >= lengthDoc) {
+ return false;
+ }
+ while (*val) {
+ if (*val != styler[pos++]) {
+ return false;
+ }
+ val++;
+ }
+ return true;
+}
+
+// Do Ruby better -- find the end of the line, work back,
+// and then check for leading white space
+
+// Precondition: the here-doc target can be indented
+static bool lookingAtHereDocDelim(Accessor &styler,
+ int pos,
+ int lengthDoc,
+ const char *HereDocDelim)
+{
+ if (!isMatch(styler, lengthDoc, pos, HereDocDelim)) {
+ return false;
+ }
+ while (--pos > 0) {
+ char ch = styler[pos];
+ if (isEOLChar(ch)) {
+ return true;
+ } else if (ch != ' ' && ch != '\t') {
+ return false;
+ }
+ }
+ return false;
+}
+
+//XXX Identical to Perl, put in common area
+static char opposite(char ch) {
+ if (ch == '(')
+ return ')';
+ if (ch == '[')
+ return ']';
+ if (ch == '{')
+ return '}';
+ if (ch == '<')
+ return '>';
+ return ch;
+}
+
+// Null transitions when we see we've reached the end
+// and need to relex the curr char.
+
+static void redo_char(int &i, char &ch, char &chNext, char &chNext2,
+ int &state) {
+ i--;
+ chNext2 = chNext;
+ chNext = ch;
+ state = SCE_RB_DEFAULT;
+}
+
+static void advance_char(int &i, char &ch, char &chNext, char &chNext2) {
+ i++;
+ ch = chNext;
+ chNext = chNext2;
+}
+
+// precondition: startPos points to one after the EOL char
+static bool currLineContainsHereDelims(int& startPos,
+ Accessor &styler) {
+ if (startPos <= 1)
+ return false;
+
+ int pos;
+ for (pos = startPos - 1; pos > 0; pos--) {
+ char ch = styler.SafeGetCharAt(pos);
+ if (isEOLChar(ch)) {
+ // Leave the pointers where they are -- there are no
+ // here doc delims on the current line, even if
+ // the EOL isn't default style
+
+ return false;
+ } else {
+ styler.Flush();
+ if (actual_style(styler.StyleAt(pos)) == SCE_RB_HERE_DELIM) {
+ break;
+ }
+ }
+ }
+ if (pos == 0) {
+ return false;
+ }
+ // Update the pointers so we don't have to re-analyze the string
+ startPos = pos;
+ return true;
+}
+
+
+static bool isEmptyLine(int pos,
+ Accessor &styler) {
+ int spaceFlags = 0;
+ int lineCurrent = styler.GetLine(pos);
+ int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, NULL);
+ return (indentCurrent & SC_FOLDLEVELWHITEFLAG) != 0;
+}
+
+static bool RE_CanFollowKeyword(const char *keyword) {
+ if (!strcmp(keyword, "and")
+ || !strcmp(keyword, "begin")
+ || !strcmp(keyword, "break")
+ || !strcmp(keyword, "case")
+ || !strcmp(keyword, "do")
+ || !strcmp(keyword, "else")
+ || !strcmp(keyword, "elsif")
+ || !strcmp(keyword, "if")
+ || !strcmp(keyword, "next")
+ || !strcmp(keyword, "return")
+ || !strcmp(keyword, "when")
+ || !strcmp(keyword, "unless")
+ || !strcmp(keyword, "until")
+ || !strcmp(keyword, "not")
+ || !strcmp(keyword, "or")) {
+ return true;
+ }
+ return false;
+}
+
+// Look at chars up to but not including endPos
+// Don't look at styles in case we're looking forward
+
+static int skipWhitespace(int startPos,
+ int endPos,
+ Accessor &styler) {
+ for (int i = startPos; i < endPos; i++) {
+ if (!iswhitespace(styler[i])) {
+ return i;
+ }
+ }
+ return endPos;
+}
+
+// This routine looks for false positives like
+// undef foo, <<
+// There aren't too many.
+//
+// iPrev points to the start of <<
+
+static bool sureThisIsHeredoc(int iPrev,
+ Accessor &styler,
+ char *prevWord) {
+
+ // Not so fast, since Ruby's so dynamic. Check the context
+ // to make sure we're OK.
+ int prevStyle;
+ int lineStart = styler.GetLine(iPrev);
+ int lineStartPosn = styler.LineStart(lineStart);
+ styler.Flush();
+
+ // Find the first word after some whitespace
+ int firstWordPosn = skipWhitespace(lineStartPosn, iPrev, styler);
+ if (firstWordPosn >= iPrev) {
+ // Have something like {^ <<}
+ //XXX Look at the first previous non-comment non-white line
+ // to establish the context. Not too likely though.
+ return true;
+ } else {
+ switch (prevStyle = styler.StyleAt(firstWordPosn)) {
+ case SCE_RB_WORD:
+ case SCE_RB_WORD_DEMOTED:
+ case SCE_RB_IDENTIFIER:
+ break;
+ default:
+ return true;
+ }
+ }
+ int firstWordEndPosn = firstWordPosn;
+ char *dst = prevWord;
+ for (;;) {
+ if (firstWordEndPosn >= iPrev ||
+ styler.StyleAt(firstWordEndPosn) != prevStyle) {
+ *dst = 0;
+ break;
+ }
+ *dst++ = styler[firstWordEndPosn];
+ firstWordEndPosn += 1;
+ }
+ //XXX Write a style-aware thing to regex scintilla buffer objects
+ if (!strcmp(prevWord, "undef")
+ || !strcmp(prevWord, "def")
+ || !strcmp(prevWord, "alias")) {
+ // These keywords are what we were looking for
+ return false;
+ }
+ return true;
+}
+
+// Routine that saves us from allocating a buffer for the here-doc target
+// targetEndPos points one past the end of the current target
+static bool haveTargetMatch(int currPos,
+ int lengthDoc,
+ int targetStartPos,
+ int targetEndPos,
+ Accessor &styler) {
+ if (lengthDoc - currPos < targetEndPos - targetStartPos) {
+ return false;
+ }
+ int i, j;
+ for (i = targetStartPos, j = currPos;
+ i < targetEndPos && j < lengthDoc;
+ i++, j++) {
+ if (styler[i] != styler[j]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+// We need a check because the form
+// [identifier] <<[target]
+// is ambiguous. The Ruby lexer/parser resolves it by
+// looking to see if [identifier] names a variable or a
+// function. If it's the first, it's the start of a here-doc.
+// If it's a var, it's an operator. This lexer doesn't
+// maintain a symbol table, so it looks ahead to see what's
+// going on, in cases where we have
+// ^[white-space]*[identifier([.|::]identifier)*][white-space]*<<[target]
+//
+// If there's no occurrence of [target] on a line, assume we don't.
+
+// return true == yes, we have no heredocs
+
+static bool sureThisIsNotHeredoc(int lt2StartPos,
+ Accessor &styler) {
+ int prevStyle;
+ // Use full document, not just part we're styling
+ int lengthDoc = styler.Length();
+ int lineStart = styler.GetLine(lt2StartPos);
+ int lineStartPosn = styler.LineStart(lineStart);
+ styler.Flush();
+ const bool definitely_not_a_here_doc = true;
+ const bool looks_like_a_here_doc = false;
+
+ // Find the first word after some whitespace
+ int firstWordPosn = skipWhitespace(lineStartPosn, lt2StartPos, styler);
+ if (firstWordPosn >= lt2StartPos) {
+ return definitely_not_a_here_doc;
+ }
+ prevStyle = styler.StyleAt(firstWordPosn);
+ // If we have '<<' following a keyword, it's not a heredoc
+ if (prevStyle != SCE_RB_IDENTIFIER) {
+ return definitely_not_a_here_doc;
+ }
+ int newStyle = prevStyle;
+ // Some compilers incorrectly warn about uninit newStyle
+ for (firstWordPosn += 1; firstWordPosn <= lt2StartPos; firstWordPosn += 1) {
+ // Inner loop looks at the name
+ for (; firstWordPosn <= lt2StartPos; firstWordPosn += 1) {
+ newStyle = styler.StyleAt(firstWordPosn);
+ if (newStyle != prevStyle) {
+ break;
+ }
+ }
+ // Do we have '::' or '.'?
+ if (firstWordPosn < lt2StartPos && newStyle == SCE_RB_OPERATOR) {
+ char ch = styler[firstWordPosn];
+ if (ch == '.') {
+ // yes
+ } else if (ch == ':') {
+ if (styler.StyleAt(++firstWordPosn) != SCE_RB_OPERATOR) {
+ return definitely_not_a_here_doc;
+ } else if (styler[firstWordPosn] != ':') {
+ return definitely_not_a_here_doc;
+ }
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ // Skip next batch of white-space
+ firstWordPosn = skipWhitespace(firstWordPosn, lt2StartPos, styler);
+ if (firstWordPosn != lt2StartPos) {
+ // Have [[^ws[identifier]ws[*something_else*]ws<<
+ return definitely_not_a_here_doc;
+ }
+ // OK, now 'j' will point to the current spot moving ahead
+ int j = firstWordPosn + 1;
+ if (styler.StyleAt(j) != SCE_RB_OPERATOR || styler[j] != '<') {
+ // This shouldn't happen
+ return definitely_not_a_here_doc;
+ }
+ int nextLineStartPosn = styler.LineStart(lineStart + 1);
+ if (nextLineStartPosn >= lengthDoc) {
+ return definitely_not_a_here_doc;
+ }
+ j = skipWhitespace(j + 1, nextLineStartPosn, styler);
+ if (j >= lengthDoc) {
+ return definitely_not_a_here_doc;
+ }
+ bool allow_indent;
+ int target_start, target_end;
+ // From this point on no more styling, since we're looking ahead
+ if (styler[j] == '-') {
+ allow_indent = true;
+ j++;
+ } else {
+ allow_indent = false;
+ }
+
+ // Allow for quoted targets.
+ char target_quote = 0;
+ switch (styler[j]) {
+ case '\'':
+ case '"':
+ case '`':
+ target_quote = styler[j];
+ j += 1;
+ }
+
+ if (isSafeAlnum(styler[j])) {
+ // Init target_end because some compilers think it won't
+ // be initialized by the time it's used
+ target_start = target_end = j;
+ j++;
+ } else {
+ return definitely_not_a_here_doc;
+ }
+ for (; j < lengthDoc; j++) {
+ if (!isSafeAlnum(styler[j])) {
+ if (target_quote && styler[j] != target_quote) {
+ // unquoted end
+ return definitely_not_a_here_doc;
+ }
+
+ // And for now make sure that it's a newline
+ // don't handle arbitrary expressions yet
+
+ target_end = j;
+ if (target_quote) {
+ // Now we can move to the character after the string delimiter.
+ j += 1;
+ }
+ j = skipWhitespace(j, lengthDoc, styler);
+ if (j >= lengthDoc) {
+ return definitely_not_a_here_doc;
+ } else {
+ char ch = styler[j];
+ if (ch == '#' || isEOLChar(ch)) {
+ // This is OK, so break and continue;
+ break;
+ } else {
+ return definitely_not_a_here_doc;
+ }
+ }
+ }
+ }
+
+ // Just look at the start of each line
+ int last_line = styler.GetLine(lengthDoc - 1);
+ // But don't go too far
+ if (last_line > lineStart + 50) {
+ last_line = lineStart + 50;
+ }
+ for (int line_num = lineStart + 1; line_num <= last_line; line_num++) {
+ if (allow_indent) {
+ j = skipWhitespace(styler.LineStart(line_num), lengthDoc, styler);
+ } else {
+ j = styler.LineStart(line_num);
+ }
+ // target_end is one past the end
+ if (haveTargetMatch(j, lengthDoc, target_start, target_end, styler)) {
+ // We got it
+ return looks_like_a_here_doc;
+ }
+ }
+ return definitely_not_a_here_doc;
+}
+
+//todo: if we aren't looking at a stdio character,
+// move to the start of the first line that is not in a
+// multi-line construct
+
+static void synchronizeDocStart(unsigned int& startPos,
+ int &length,
+ int &initStyle,
+ Accessor &styler,
+ bool skipWhiteSpace=false) {
+
+ styler.Flush();
+ int style = actual_style(styler.StyleAt(startPos));
+ switch (style) {
+ case SCE_RB_STDIN:
+ case SCE_RB_STDOUT:
+ case SCE_RB_STDERR:
+ // Don't do anything else with these.
+ return;
+ }
+
+ int pos = startPos;
+ // Quick way to characterize each line
+ int lineStart;
+ for (lineStart = styler.GetLine(pos); lineStart > 0; lineStart--) {
+ // Now look at the style before the previous line's EOL
+ pos = styler.LineStart(lineStart) - 1;
+ if (pos <= 10) {
+ lineStart = 0;
+ break;
+ }
+ char ch = styler.SafeGetCharAt(pos);
+ char chPrev = styler.SafeGetCharAt(pos - 1);
+ if (ch == '\n' && chPrev == '\r') {
+ pos--;
+ }
+ if (styler.SafeGetCharAt(pos - 1) == '\\') {
+ // Continuation line -- keep going
+ } else if (actual_style(styler.StyleAt(pos)) != SCE_RB_DEFAULT) {
+ // Part of multi-line construct -- keep going
+ } else if (currLineContainsHereDelims(pos, styler)) {
+ // Keep going, with pos and length now pointing
+ // at the end of the here-doc delimiter
+ } else if (skipWhiteSpace && isEmptyLine(pos, styler)) {
+ // Keep going
+ } else {
+ break;
+ }
+ }
+ pos = styler.LineStart(lineStart);
+ length += (startPos - pos);
+ startPos = pos;
+ initStyle = SCE_RB_DEFAULT;
+}
+
+static void ColouriseRbDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler) {
+
+ // Lexer for Ruby often has to backtrack to start of current style to determine
+ // which characters are being used as quotes, how deeply nested is the
+ // start position and what the termination string is for here documents
+
+ WordList &keywords = *keywordlists[0];
+
+ class HereDocCls {
+ public:
+ int State;
+ // States
+ // 0: '<<' encountered
+ // 1: collect the delimiter
+ // 1b: text between the end of the delimiter and the EOL
+ // 2: here doc text (lines after the delimiter)
+ char Quote; // the char after '<<'
+ bool Quoted; // true if Quote in ('\'','"','`')
+ int DelimiterLength; // strlen(Delimiter)
+ char Delimiter[256]; // the Delimiter, limit of 256: from Perl
+ bool CanBeIndented;
+ HereDocCls() {
+ State = 0;
+ DelimiterLength = 0;
+ Delimiter[0] = '\0';
+ CanBeIndented = false;
+ }
+ };
+ HereDocCls HereDoc;
+
+ class QuoteCls {
+ public:
+ int Count;
+ char Up;
+ char Down;
+ QuoteCls() {
+ this->New();
+ }
+ void New() {
+ Count = 0;
+ Up = '\0';
+ Down = '\0';
+ }
+ void Open(char u) {
+ Count++;
+ Up = u;
+ Down = opposite(Up);
+ }
+ };
+ QuoteCls Quote;
+
+ int numDots = 0; // For numbers --
+ // Don't start lexing in the middle of a num
+
+ synchronizeDocStart(startPos, length, initStyle, styler, // ref args
+ false);
+
+ bool preferRE = true;
+ int state = initStyle;
+ int lengthDoc = startPos + length;
+
+ char prevWord[MAX_KEYWORD_LENGTH + 1]; // 1 byte for zero
+ prevWord[0] = '\0';
+ if (length == 0)
+ return;
+
+ char chPrev = styler.SafeGetCharAt(startPos - 1);
+ char chNext = styler.SafeGetCharAt(startPos);
+ // Ruby uses a different mask because bad indentation is marked by oring with 32
+ styler.StartAt(startPos, 127);
+ styler.StartSegment(startPos);
+
+ static int q_states[] = {SCE_RB_STRING_Q,
+ SCE_RB_STRING_QQ,
+ SCE_RB_STRING_QR,
+ SCE_RB_STRING_QW,
+ SCE_RB_STRING_QW,
+ SCE_RB_STRING_QX};
+ static const char* q_chars = "qQrwWx";
+
+ for (int i = startPos; i < lengthDoc; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ char chNext2 = styler.SafeGetCharAt(i + 2);
+
+ if (styler.IsLeadByte(ch)) {
+ chNext = chNext2;
+ chPrev = ' ';
+ i += 1;
+ continue;
+ }
+
+ // skip on DOS/Windows
+ //No, don't, because some things will get tagged on,
+ // so we won't recognize keywords, for example
+#if 0
+ if (ch == '\r' && chNext == '\n') {
+ continue;
+ }
+#endif
+
+ if (HereDoc.State == 1 && isEOLChar(ch)) {
+ // Begin of here-doc (the line after the here-doc delimiter):
+ HereDoc.State = 2;
+ styler.ColourTo(i-1, state);
+ // Don't check for a missing quote, just jump into
+ // the here-doc state
+ state = SCE_RB_HERE_Q;
+ }
+
+ // Regular transitions
+ if (state == SCE_RB_DEFAULT) {
+ if (isSafeDigit(ch)) {
+ styler.ColourTo(i - 1, state);
+ state = SCE_RB_NUMBER;
+ numDots = 0;
+ } else if (isHighBitChar(ch) || iswordstart(ch)) {
+ styler.ColourTo(i - 1, state);
+ state = SCE_RB_WORD;
+ } else if (ch == '#') {
+ styler.ColourTo(i - 1, state);
+ state = SCE_RB_COMMENTLINE;
+ } else if (ch == '=') {
+ // =begin indicates the start of a comment (doc) block
+ if (i == 0 || isEOLChar(chPrev)
+ && chNext == 'b'
+ && styler.SafeGetCharAt(i + 2) == 'e'
+ && styler.SafeGetCharAt(i + 3) == 'g'
+ && styler.SafeGetCharAt(i + 4) == 'i'
+ && styler.SafeGetCharAt(i + 5) == 'n'
+ && !isSafeWordcharOrHigh(styler.SafeGetCharAt(i + 6))) {
+ styler.ColourTo(i - 1, state);
+ state = SCE_RB_POD;
+ } else {
+ styler.ColourTo(i - 1, state);
+ styler.ColourTo(i, SCE_RB_OPERATOR);
+ preferRE = true;
+ }
+ } else if (ch == '"') {
+ styler.ColourTo(i - 1, state);
+ state = SCE_RB_STRING;
+ Quote.New();
+ Quote.Open(ch);
+ } else if (ch == '\'') {
+ styler.ColourTo(i - 1, state);
+ state = SCE_RB_CHARACTER;
+ Quote.New();
+ Quote.Open(ch);
+ } else if (ch == '`') {
+ styler.ColourTo(i - 1, state);
+ state = SCE_RB_BACKTICKS;
+ Quote.New();
+ Quote.Open(ch);
+ } else if (ch == '@') {
+ // Instance or class var
+ styler.ColourTo(i - 1, state);
+ if (chNext == '@') {
+ state = SCE_RB_CLASS_VAR;
+ advance_char(i, ch, chNext, chNext2); // pass by ref
+ } else {
+ state = SCE_RB_INSTANCE_VAR;
+ }
+ } else if (ch == '$') {
+ // Check for a builtin global
+ styler.ColourTo(i - 1, state);
+ // Recognize it bit by bit
+ state = SCE_RB_GLOBAL;
+ } else if (ch == '/' && preferRE) {
+ // Ambigous operator
+ styler.ColourTo(i - 1, state);
+ state = SCE_RB_REGEX;
+ Quote.New();
+ Quote.Open(ch);
+ } else if (ch == '<' && chNext == '<' && chNext2 != '=') {
+
+ // Recognise the '<<' symbol - either a here document or a binary op
+ styler.ColourTo(i - 1, state);
+ i++;
+ chNext = chNext2;
+ styler.ColourTo(i, SCE_RB_OPERATOR);
+
+ if (! (strchr("\"\'`_-", chNext2) || isSafeAlpha(chNext2))) {
+ // It's definitely not a here-doc,
+ // based on Ruby's lexer/parser in the
+ // heredoc_identifier routine.
+ // Nothing else to do.
+ } else if (preferRE) {
+ if (sureThisIsHeredoc(i - 1, styler, prevWord)) {
+ state = SCE_RB_HERE_DELIM;
+ HereDoc.State = 0;
+ }
+ // else leave it in default state
+ } else {
+ if (sureThisIsNotHeredoc(i - 1, styler)) {
+ // leave state as default
+ // We don't have all the heuristics Perl has for indications
+ // of a here-doc, because '<<' is overloadable and used
+ // for so many other classes.
+ } else {
+ state = SCE_RB_HERE_DELIM;
+ HereDoc.State = 0;
+ }
+ }
+ preferRE = (state != SCE_RB_HERE_DELIM);
+ } else if (ch == ':') {
+ styler.ColourTo(i - 1, state);
+ if (chNext == ':') {
+ // Mark "::" as an operator, not symbol start
+ styler.ColourTo(i + 1, SCE_RB_OPERATOR);
+ advance_char(i, ch, chNext, chNext2); // pass by ref
+ state = SCE_RB_DEFAULT;
+ preferRE = false;
+ } else if (isSafeWordcharOrHigh(chNext)) {
+ state = SCE_RB_SYMBOL;
+ } else if (strchr("[*!~+-*/%=<>&^|", chNext)) {
+ // Do the operator analysis in-line, looking ahead
+ // Based on the table in pickaxe 2nd ed., page 339
+ bool doColoring = true;
+ switch (chNext) {
+ case '[':
+ if (chNext2 == ']' ) {
+ char ch_tmp = styler.SafeGetCharAt(i + 3);
+ if (ch_tmp == '=') {
+ i += 3;
+ ch = ch_tmp;
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else {
+ i += 2;
+ ch = chNext2;
+ chNext = ch_tmp;
+ }
+ } else {
+ doColoring = false;
+ }
+ break;
+
+ case '*':
+ if (chNext2 == '*') {
+ i += 2;
+ ch = chNext2;
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else {
+ advance_char(i, ch, chNext, chNext2);
+ }
+ break;
+
+ case '!':
+ if (chNext2 == '=' || chNext2 == '~') {
+ i += 2;
+ ch = chNext2;
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else {
+ advance_char(i, ch, chNext, chNext2);
+ }
+ break;
+
+ case '<':
+ if (chNext2 == '<') {
+ i += 2;
+ ch = chNext2;
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else if (chNext2 == '=') {
+ char ch_tmp = styler.SafeGetCharAt(i + 3);
+ if (ch_tmp == '>') { // <=> operator
+ i += 3;
+ ch = ch_tmp;
+ chNext = styler.SafeGetCharAt(i + 1);
+ } else {
+ i += 2;
+ ch = chNext2;
+ chNext = ch_tmp;
+ }
+ } else {
+ advance_char(i, ch, chNext, chNext2);
+ }
+ break;
+
+ default:
+ // Simple one-character operators
+ advance_char(i, ch, chNext, chNext2);
+ break;
+ }
+ if (doColoring) {
+ styler.ColourTo(i, SCE_RB_SYMBOL);
+ state = SCE_RB_DEFAULT;
+ }
+ } else if (!preferRE) {
+ // Don't color symbol strings (yet)
+ // Just color the ":" and color rest as string
+ styler.ColourTo(i, SCE_RB_SYMBOL);
+ state = SCE_RB_DEFAULT;
+ } else {
+ styler.ColourTo(i, SCE_RB_OPERATOR);
+ state = SCE_RB_DEFAULT;
+ preferRE = true;
+ }
+ } else if (ch == '%') {
+ styler.ColourTo(i - 1, state);
+ bool have_string = false;
+ if (strchr(q_chars, chNext) && !isSafeWordcharOrHigh(chNext2)) {
+ Quote.New();
+ const char *hit = strchr(q_chars, chNext);
+ if (hit != NULL) {
+ state = q_states[hit - q_chars];
+ Quote.Open(chNext2);
+ i += 2;
+ ch = chNext2;
+ chNext = styler.SafeGetCharAt(i + 1);
+ have_string = true;
+ }
+ } else if (!isSafeWordcharOrHigh(chNext)) {
+ // Ruby doesn't allow high bit chars here,
+ // but the editor host might
+ state = SCE_RB_STRING_QQ;
+ Quote.Open(chNext);
+ advance_char(i, ch, chNext, chNext2); // pass by ref
+ have_string = true;
+ }
+ if (!have_string) {
+ styler.ColourTo(i, SCE_RB_OPERATOR);
+ // stay in default
+ preferRE = true;
+ }
+ } else if (isoperator(ch) || ch == '.') {
+ styler.ColourTo(i - 1, state);
+ styler.ColourTo(i, SCE_RB_OPERATOR);
+ // If we're ending an expression or block,
+ // assume it ends an object, and the ambivalent
+ // constructs are binary operators
+ //
+ // So if we don't have one of these chars,
+ // we aren't ending an object exp'n, and ops
+ // like : << / are unary operators.
+
+ preferRE = (strchr(")}].", ch) == NULL);
+ // Stay in default state
+ } else if (isEOLChar(ch)) {
+ // Make sure it's a true line-end, with no backslash
+ if ((ch == '\r' || (ch == '\n' && chPrev != '\r'))
+ && chPrev != '\\') {
+ // Assume we've hit the end of the statement.
+ preferRE = true;
+ }
+ }
+ } else if (state == SCE_RB_WORD) {
+ if (ch == '.' || !isSafeWordcharOrHigh(ch)) {
+ // Words include x? in all contexts,
+ // and <letters>= after either 'def' or a dot
+ // Move along until a complete word is on our left
+
+ // Default accessor treats '.' as word-chars,
+ // but we don't for now.
+
+ if (ch == '='
+ && isSafeWordcharOrHigh(chPrev)
+ && (chNext == '('
+ || strchr(" \t\n\r", chNext) != NULL)
+ && (!strcmp(prevWord, "def")
+ || followsDot(styler.GetStartSegment(), styler))) {
+ // <name>= is a name only when being def'd -- Get it the next time
+ // This means that <name>=<name> is always lexed as
+ // <name>, (op, =), <name>
+ } else if ((ch == '?' || ch == '!')
+ && isSafeWordcharOrHigh(chPrev)
+ && !isSafeWordcharOrHigh(chNext)) {
+ // <name>? is a name -- Get it the next time
+ // But <name>?<name> is always lexed as
+ // <name>, (op, ?), <name>
+ // Same with <name>! to indicate a method that
+ // modifies its target
+ } else if (isEOLChar(ch)
+ && isMatch(styler, lengthDoc, i - 7, "__END__")) {
+ styler.ColourTo(i, SCE_RB_DATASECTION);
+ state = SCE_RB_DATASECTION;
+ // No need to handle this state -- we'll just move to the end
+ preferRE = false;
+ } else {
+ int wordStartPos = styler.GetStartSegment();
+ int word_style = ClassifyWordRb(wordStartPos, i - 1, keywords, styler, prevWord);
+ switch (word_style) {
+ case SCE_RB_WORD:
+ preferRE = RE_CanFollowKeyword(prevWord);
+ break;
+
+ case SCE_RB_WORD_DEMOTED:
+ preferRE = true;
+ break;
+
+ case SCE_RB_IDENTIFIER:
+ if (isMatch(styler, lengthDoc, wordStartPos, "print")) {
+ preferRE = true;
+ } else if (isEOLChar(ch)) {
+ preferRE = true;
+ } else {
+ preferRE = false;
+ }
+ break;
+ default:
+ preferRE = false;
+ }
+ if (ch == '.') {
+ // We might be redefining an operator-method
+ preferRE = false;
+ }
+ // And if it's the first
+ redo_char(i, ch, chNext, chNext2, state); // pass by ref
+ }
+ }
+ } else if (state == SCE_RB_NUMBER) {
+ if (isSafeAlnumOrHigh(ch) || ch == '_') {
+ // Keep going
+ } else if (ch == '.' && ++numDots == 1) {
+ // Keep going
+ } else {
+ styler.ColourTo(i - 1, state);
+ redo_char(i, ch, chNext, chNext2, state); // pass by ref
+ preferRE = false;
+ }
+ } else if (state == SCE_RB_COMMENTLINE) {
+ if (isEOLChar(ch)) {
+ styler.ColourTo(i - 1, state);
+ state = SCE_RB_DEFAULT;
+ // Use whatever setting we had going into the comment
+ }
+ } else if (state == SCE_RB_HERE_DELIM) {
+ // See the comment for SCE_RB_HERE_DELIM in LexPerl.cxx
+ // Slightly different: if we find an immediate '-',
+ // the target can appear indented.
+
+ if (HereDoc.State == 0) { // '<<' encountered
+ HereDoc.State = 1;
+ HereDoc.DelimiterLength = 0;
+ if (ch == '-') {
+ HereDoc.CanBeIndented = true;
+ advance_char(i, ch, chNext, chNext2); // pass by ref
+ } else {
+ HereDoc.CanBeIndented = false;
+ }
+ if (isEOLChar(ch)) {
+ // Bail out of doing a here doc if there's no target
+ state = SCE_RB_DEFAULT;
+ preferRE = false;
+ } else {
+ HereDoc.Quote = ch;
+
+ if (ch == '\'' || ch == '"' || ch == '`') {
+ HereDoc.Quoted = true;
+ HereDoc.Delimiter[0] = '\0';
+ } else {
+ HereDoc.Quoted = false;
+ HereDoc.Delimiter[0] = ch;
+ HereDoc.Delimiter[1] = '\0';
+ HereDoc.DelimiterLength = 1;
+ }
+ }
+ } else if (HereDoc.State == 1) { // collect the delimiter
+ if (isEOLChar(ch)) {
+ // End the quote now, and go back for more
+ styler.ColourTo(i - 1, state);
+ state = SCE_RB_DEFAULT;
+ i--;
+ chNext = ch;
+ chNext2 = chNext;
+ preferRE = false;
+ } else if (HereDoc.Quoted) {
+ if (ch == HereDoc.Quote) { // closing quote => end of delimiter
+ styler.ColourTo(i, state);
+ state = SCE_RB_DEFAULT;
+ preferRE = false;
+ } else {
+ if (ch == '\\' && !isEOLChar(chNext)) {
+ advance_char(i, ch, chNext, chNext2);
+ }
+ HereDoc.Delimiter[HereDoc.DelimiterLength++] = ch;
+ HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0';
+ }
+ } else { // an unquoted here-doc delimiter
+ if (isSafeAlnumOrHigh(ch) || ch == '_') {
+ HereDoc.Delimiter[HereDoc.DelimiterLength++] = ch;
+ HereDoc.Delimiter[HereDoc.DelimiterLength] = '\0';
+ } else {
+ styler.ColourTo(i - 1, state);
+ redo_char(i, ch, chNext, chNext2, state);
+ preferRE = false;
+ }
+ }
+ if (HereDoc.DelimiterLength >= static_cast<int>(sizeof(HereDoc.Delimiter)) - 1) {
+ styler.ColourTo(i - 1, state);
+ state = SCE_RB_ERROR;
+ preferRE = false;
+ }
+ }
+ } else if (state == SCE_RB_HERE_Q) {
+ // Not needed: HereDoc.State == 2
+ // Indentable here docs: look backwards
+ // Non-indentable: look forwards, like in Perl
+ //
+ // Why: so we can quickly resolve things like <<-" abc"
+
+ if (!HereDoc.CanBeIndented) {
+ if (isEOLChar(chPrev)
+ && isMatch(styler, lengthDoc, i, HereDoc.Delimiter)) {
+ styler.ColourTo(i - 1, state);
+ i += HereDoc.DelimiterLength - 1;
+ chNext = styler.SafeGetCharAt(i + 1);
+ if (isEOLChar(chNext)) {
+ styler.ColourTo(i, SCE_RB_HERE_DELIM);
+ state = SCE_RB_DEFAULT;
+ HereDoc.State = 0;
+ preferRE = false;
+ }
+ // Otherwise we skipped through the here doc faster.
+ }
+ } else if (isEOLChar(chNext)
+ && lookingAtHereDocDelim(styler,
+ i - HereDoc.DelimiterLength + 1,
+ lengthDoc,
+ HereDoc.Delimiter)) {
+ styler.ColourTo(i - 1 - HereDoc.DelimiterLength, state);
+ styler.ColourTo(i, SCE_RB_HERE_DELIM);
+ state = SCE_RB_DEFAULT;
+ preferRE = false;
+ HereDoc.State = 0;
+ }
+ } else if (state == SCE_RB_CLASS_VAR
+ || state == SCE_RB_INSTANCE_VAR
+ || state == SCE_RB_SYMBOL) {
+ if (!isSafeWordcharOrHigh(ch)) {
+ styler.ColourTo(i - 1, state);
+ redo_char(i, ch, chNext, chNext2, state); // pass by ref
+ preferRE = false;
+ }
+ } else if (state == SCE_RB_GLOBAL) {
+ if (!isSafeWordcharOrHigh(ch)) {
+ // handle special globals here as well
+ if (chPrev == '$') {
+ if (ch == '-') {
+ // Include the next char, like $-a
+ advance_char(i, ch, chNext, chNext2);
+ }
+ styler.ColourTo(i, state);
+ state = SCE_RB_DEFAULT;
+ } else {
+ styler.ColourTo(i - 1, state);
+ redo_char(i, ch, chNext, chNext2, state); // pass by ref
+ }
+ preferRE = false;
+ }
+ } else if (state == SCE_RB_POD) {
+ // PODs end with ^=end\s, -- any whitespace can follow =end
+ if (strchr(" \t\n\r", ch) != NULL
+ && i > 5
+ && isEOLChar(styler[i - 5])
+ && isMatch(styler, lengthDoc, i - 4, "=end")) {
+ styler.ColourTo(i - 1, state);
+ state = SCE_RB_DEFAULT;
+ preferRE = false;
+ }
+ } else if (state == SCE_RB_REGEX || state == SCE_RB_STRING_QR) {
+ if (ch == '\\' && Quote.Up != '\\') {
+ // Skip one
+ advance_char(i, ch, chNext, chNext2);
+ } else if (ch == Quote.Down) {
+ Quote.Count--;
+ if (Quote.Count == 0) {
+ // Include the options
+ while (isSafeAlpha(chNext)) {
+ i++;
+ ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ styler.ColourTo(i, state);
+ state = SCE_RB_DEFAULT;
+ preferRE = false;
+ }
+ } else if (ch == Quote.Up) {
+ // Only if close quoter != open quoter
+ Quote.Count++;
+
+ } else if (ch == '#' ) {
+ //todo: distinguish comments from pound chars
+ // for now, handle as comment
+ styler.ColourTo(i - 1, state);
+ bool inEscape = false;
+ while (++i < lengthDoc) {
+ ch = styler.SafeGetCharAt(i);
+ if (ch == '\\') {
+ inEscape = true;
+ } else if (isEOLChar(ch)) {
+ // Comment inside a regex
+ styler.ColourTo(i - 1, SCE_RB_COMMENTLINE);
+ break;
+ } else if (inEscape) {
+ inEscape = false; // don't look at char
+ } else if (ch == Quote.Down) {
+ // Have the regular handler deal with this
+ // to get trailing modifiers.
+ i--;
+ ch = styler[i];
+ break;
+ }
+ }
+ chNext = styler.SafeGetCharAt(i + 1);
+ chNext2 = styler.SafeGetCharAt(i + 2);
+ }
+ // Quotes of all kinds...
+ } else if (state == SCE_RB_STRING_Q || state == SCE_RB_STRING_QQ ||
+ state == SCE_RB_STRING_QX || state == SCE_RB_STRING_QW ||
+ state == SCE_RB_STRING || state == SCE_RB_CHARACTER ||
+ state == SCE_RB_BACKTICKS) {
+ if (!Quote.Down && !isspacechar(ch)) {
+ Quote.Open(ch);
+ } else if (ch == '\\' && Quote.Up != '\\') {
+ //Riddle me this: Is it safe to skip *every* escaped char?
+ advance_char(i, ch, chNext, chNext2);
+ } else if (ch == Quote.Down) {
+ Quote.Count--;
+ if (Quote.Count == 0) {
+ styler.ColourTo(i, state);
+ state = SCE_RB_DEFAULT;
+ preferRE = false;
+ }
+ } else if (ch == Quote.Up) {
+ Quote.Count++;
+ }
+ }
+
+ if (state == SCE_RB_ERROR) {
+ break;
+ }
+ chPrev = ch;
+ }
+ if (state == SCE_RB_WORD) {
+ // We've ended on a word, possibly at EOF, and need to
+ // classify it.
+ (void) ClassifyWordRb(styler.GetStartSegment(), lengthDoc - 1, keywords, styler, prevWord);
+ } else {
+ styler.ColourTo(lengthDoc - 1, state);
+ }
+}
+
+// Helper functions for folding, disambiguation keywords
+// Assert that there are no high-bit chars
+
+static void getPrevWord(int pos,
+ char *prevWord,
+ Accessor &styler,
+ int word_state)
+{
+ int i;
+ styler.Flush();
+ for (i = pos - 1; i > 0; i--) {
+ if (actual_style(styler.StyleAt(i)) != word_state) {
+ i++;
+ break;
+ }
+ }
+ if (i < pos - MAX_KEYWORD_LENGTH) // overflow
+ i = pos - MAX_KEYWORD_LENGTH;
+ char *dst = prevWord;
+ for (; i <= pos; i++) {
+ *dst++ = styler[i];
+ }
+ *dst = 0;
+}
+
+static bool keywordIsAmbiguous(const char *prevWord)
+{
+ // Order from most likely used to least likely
+ // Lots of ways to do a loop in Ruby besides 'while/until'
+ if (!strcmp(prevWord, "if")
+ || !strcmp(prevWord, "do")
+ || !strcmp(prevWord, "while")
+ || !strcmp(prevWord, "unless")
+ || !strcmp(prevWord, "until")) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+// Demote keywords in the following conditions:
+// if, while, unless, until modify a statement
+// do after a while or until, as a noise word (like then after if)
+
+static bool keywordIsModifier(const char *word,
+ int pos,
+ Accessor &styler)
+{
+ if (word[0] == 'd' && word[1] == 'o' && !word[2]) {
+ return keywordDoStartsLoop(pos, styler);
+ }
+ char ch;
+ int style = SCE_RB_DEFAULT;
+ int lineStart = styler.GetLine(pos);
+ int lineStartPosn = styler.LineStart(lineStart);
+ styler.Flush();
+ while (--pos >= lineStartPosn) {
+ style = actual_style(styler.StyleAt(pos));
+ if (style == SCE_RB_DEFAULT) {
+ if (iswhitespace(ch = styler[pos])) {
+ //continue
+ } else if (ch == '\r' || ch == '\n') {
+ // Scintilla's LineStart() and GetLine() routines aren't
+ // platform-independent, so if we have text prepared with
+ // a different system we can't rely on it.
+ return false;
+ }
+ } else {
+ break;
+ }
+ }
+ if (pos < lineStartPosn) {
+ return false; //XXX not quite right if the prev line is a continuation
+ }
+ // First things where the action is unambiguous
+ switch (style) {
+ case SCE_RB_DEFAULT:
+ case SCE_RB_COMMENTLINE:
+ case SCE_RB_POD:
+ case SCE_RB_CLASSNAME:
+ case SCE_RB_DEFNAME:
+ case SCE_RB_MODULE_NAME:
+ return false;
+ case SCE_RB_OPERATOR:
+ break;
+ case SCE_RB_WORD:
+ // Watch out for uses of 'else if'
+ //XXX: Make a list of other keywords where 'if' isn't a modifier
+ // and can appear legitimately
+ // Formulate this to avoid warnings from most compilers
+ if (strcmp(word, "if") == 0) {
+ char prevWord[MAX_KEYWORD_LENGTH + 1];
+ getPrevWord(pos, prevWord, styler, SCE_RB_WORD);
+ return strcmp(prevWord, "else") != 0;
+ }
+ return true;
+ default:
+ return true;
+ }
+ // Assume that if the keyword follows an operator,
+ // usually it's a block assignment, like
+ // a << if x then y else z
+
+ ch = styler[pos];
+ switch (ch) {
+ case ')':
+ case ']':
+ case '}':
+ return true;
+ default:
+ return false;
+ }
+}
+
+#define WHILE_BACKWARDS "elihw"
+#define UNTIL_BACKWARDS "litnu"
+
+// Nothing fancy -- look to see if we follow a while/until somewhere
+// on the current line
+
+static bool keywordDoStartsLoop(int pos,
+ Accessor &styler)
+{
+ char ch;
+ int style;
+ int lineStart = styler.GetLine(pos);
+ int lineStartPosn = styler.LineStart(lineStart);
+ styler.Flush();
+ while (--pos >= lineStartPosn) {
+ style = actual_style(styler.StyleAt(pos));
+ if (style == SCE_RB_DEFAULT) {
+ if ((ch = styler[pos]) == '\r' || ch == '\n') {
+ // Scintilla's LineStart() and GetLine() routines aren't
+ // platform-independent, so if we have text prepared with
+ // a different system we can't rely on it.
+ return false;
+ }
+ } else if (style == SCE_RB_WORD) {
+ // Check for while or until, but write the word in backwards
+ char prevWord[MAX_KEYWORD_LENGTH + 1]; // 1 byte for zero
+ char *dst = prevWord;
+ int wordLen = 0;
+ int start_word;
+ for (start_word = pos;
+ start_word >= lineStartPosn && actual_style(styler.StyleAt(start_word)) == SCE_RB_WORD;
+ start_word--) {
+ if (++wordLen < MAX_KEYWORD_LENGTH) {
+ *dst++ = styler[start_word];
+ }
+ }
+ *dst = 0;
+ // Did we see our keyword?
+ if (!strcmp(prevWord, WHILE_BACKWARDS)
+ || !strcmp(prevWord, UNTIL_BACKWARDS)) {
+ return true;
+ }
+ // We can move pos to the beginning of the keyword, and then
+ // accept another decrement, as we can never have two contiguous
+ // keywords:
+ // word1 word2
+ // ^
+ // <- move to start_word
+ // ^
+ // <- loop decrement
+ // ^ # pointing to end of word1 is fine
+ pos = start_word;
+ }
+ }
+ return false;
+}
+
+/*
+ * Folding Ruby
+ *
+ * The language is quite complex to analyze without a full parse.
+ * For example, this line shouldn't affect fold level:
+ *
+ * print "hello" if feeling_friendly?
+ *
+ * Neither should this:
+ *
+ * print "hello" \
+ * if feeling_friendly?
+ *
+ *
+ * But this should:
+ *
+ * if feeling_friendly? #++
+ * print "hello" \
+ * print "goodbye"
+ * end #--
+ *
+ * So we cheat, by actually looking at the existing indentation
+ * levels for each line, and just echoing it back. Like Python.
+ * Then if we get better at it, we'll take braces into consideration,
+ * which always affect folding levels.
+
+ * How the keywords should work:
+ * No effect:
+ * __FILE__ __LINE__ BEGIN END alias and
+ * defined? false in nil not or self super then
+ * true undef
+
+ * Always increment:
+ * begin class def do for module when {
+ *
+ * Always decrement:
+ * end }
+ *
+ * Increment if these start a statement
+ * if unless until while -- do nothing if they're modifiers
+
+ * These end a block if there's no modifier, but don't bother
+ * break next redo retry return yield
+ *
+ * These temporarily de-indent, but re-indent
+ * case else elsif ensure rescue
+ *
+ * This means that the folder reflects indentation rather
+ * than setting it. The language-service updates indentation
+ * when users type return and finishes entering de-denters.
+ *
+ * Later offer to fold POD, here-docs, strings, and blocks of comments
+ */
+
+static void FoldRbDoc(unsigned int startPos, int length, int initStyle,
+ WordList *[], Accessor &styler) {
+ const bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
+
+ synchronizeDocStart(startPos, length, initStyle, styler, // ref args
+ false);
+ unsigned int endPos = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelPrev = startPos == 0 ? 0 : (styler.LevelAt(lineCurrent)
+ & SC_FOLDLEVELNUMBERMASK
+ & ~SC_FOLDLEVELBASE);
+ int levelCurrent = levelPrev;
+ char chNext = styler[startPos];
+ int styleNext = styler.StyleAt(startPos);
+ int stylePrev = startPos <= 1 ? SCE_RB_DEFAULT : styler.StyleAt(startPos - 1);
+ bool buffer_ends_with_eol = false;
+ for (unsigned int i = startPos; i < endPos; 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_RB_COMMENTLINE) {
+ if (foldComment && stylePrev != SCE_RB_COMMENTLINE) {
+ if (chNext == '{') {
+ levelCurrent++;
+ } else if (chNext == '}') {
+ levelCurrent--;
+ }
+ }
+ } else if (style == SCE_RB_OPERATOR) {
+ if (strchr("[{(", ch)) {
+ levelCurrent++;
+ } else if (strchr(")}]", ch)) {
+ // Don't decrement below 0
+ if (levelCurrent > 0)
+ levelCurrent--;
+ }
+ } else if (style == SCE_RB_WORD && styleNext != SCE_RB_WORD) {
+ // Look at the keyword on the left and decide what to do
+ char prevWord[MAX_KEYWORD_LENGTH + 1]; // 1 byte for zero
+ prevWord[0] = 0;
+ getPrevWord(i, prevWord, styler, SCE_RB_WORD);
+ if (!strcmp(prevWord, "end")) {
+ // Don't decrement below 0
+ if (levelCurrent > 0)
+ levelCurrent--;
+ } else if ( !strcmp(prevWord, "if")
+ || !strcmp(prevWord, "def")
+ || !strcmp(prevWord, "class")
+ || !strcmp(prevWord, "module")
+ || !strcmp(prevWord, "begin")
+ || !strcmp(prevWord, "case")
+ || !strcmp(prevWord, "do")
+ || !strcmp(prevWord, "while")
+ || !strcmp(prevWord, "unless")
+ || !strcmp(prevWord, "until")
+ || !strcmp(prevWord, "for")
+ ) {
+ levelCurrent++;
+ }
+ }
+ if (atEOL) {
+ int lev = levelPrev;
+ if (visibleChars == 0 && foldCompact)
+ lev |= SC_FOLDLEVELWHITEFLAG;
+ if ((levelCurrent > levelPrev) && (visibleChars > 0))
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ styler.SetLevel(lineCurrent, lev|SC_FOLDLEVELBASE);
+ lineCurrent++;
+ levelPrev = levelCurrent;
+ visibleChars = 0;
+ buffer_ends_with_eol = true;
+ } else if (!isspacechar(ch)) {
+ visibleChars++;
+ buffer_ends_with_eol = false;
+ }
+ }
+ // Fill in the real level of the next line, keeping the current flags as they will be filled in later
+ if (!buffer_ends_with_eol) {
+ lineCurrent++;
+ int new_lev = levelCurrent;
+ if (visibleChars == 0 && foldCompact)
+ new_lev |= SC_FOLDLEVELWHITEFLAG;
+ if ((levelCurrent > levelPrev) && (visibleChars > 0))
+ new_lev |= SC_FOLDLEVELHEADERFLAG;
+ levelCurrent = new_lev;
+ }
+ styler.SetLevel(lineCurrent, levelCurrent|SC_FOLDLEVELBASE);
+}
+
+static const char * const rubyWordListDesc[] = {
+ "Keywords",
+ 0
+};
+
+LexerModule lmRuby(SCLEX_RUBY, ColouriseRbDoc, "ruby", FoldRbDoc, rubyWordListDesc);
diff --git a/src/LexSQL.cpp b/src/LexSQL.cpp
new file mode 100755
index 0000000..d8e14b9
--- /dev/null
+++ b/src/LexSQL.cpp
@@ -0,0 +1,342 @@
+// Scintilla source code edit control
+/** @file LexSQL.cxx
+ ** Lexer for SQL, including PL/SQL and SQL*Plus.
+ **/
+// Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+static inline bool IsAWordChar(int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_');
+}
+
+static inline bool IsAWordStart(int ch) {
+ return (ch < 0x80) && (isalpha(ch) || ch == '_');
+}
+
+static inline bool IsADoxygenChar(int ch) {
+ return (islower(ch) || ch == '$' || ch == '@' ||
+ ch == '\\' || ch == '&' || ch == '<' ||
+ ch == '>' || ch == '#' || ch == '{' ||
+ ch == '}' || ch == '[' || ch == ']');
+}
+
+static inline bool IsANumberChar(int ch) {
+ // Not exactly following number definition (several dots are seen as OK, etc.)
+ // but probably enough in most cases.
+ return (ch < 0x80) &&
+ (isdigit(ch) || toupper(ch) == 'E' ||
+ ch == '.' || ch == '-' || ch == '+');
+}
+
+static void ColouriseSQLDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+ Accessor &styler) {
+
+ WordList &keywords1 = *keywordlists[0];
+ WordList &keywords2 = *keywordlists[1];
+ WordList &kw_pldoc = *keywordlists[2];
+ WordList &kw_sqlplus = *keywordlists[3];
+ WordList &kw_user1 = *keywordlists[4];
+ WordList &kw_user2 = *keywordlists[5];
+ WordList &kw_user3 = *keywordlists[6];
+ WordList &kw_user4 = *keywordlists[7];
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ bool sqlBackslashEscapes = styler.GetPropertyInt("sql.backslash.escapes", 0) != 0;
+ bool sqlBackticksIdentifier = styler.GetPropertyInt("lexer.sql.backticks.identifier", 0) != 0;
+ int styleBeforeDCKeyword = SCE_SQL_DEFAULT;
+ for (; sc.More(); sc.Forward()) {
+ // Determine if the current state should terminate.
+ switch (sc.state) {
+ case SCE_SQL_OPERATOR:
+ sc.SetState(SCE_SQL_DEFAULT);
+ break;
+ case SCE_SQL_NUMBER:
+ // We stop the number definition on non-numerical non-dot non-eE non-sign char
+ if (!IsANumberChar(sc.ch)) {
+ sc.SetState(SCE_SQL_DEFAULT);
+ }
+ break;
+ case SCE_SQL_IDENTIFIER:
+ if (!IsAWordChar(sc.ch)) {
+ int nextState = SCE_SQL_DEFAULT;
+ char s[1000];
+ sc.GetCurrentLowered(s, sizeof(s));
+ if (keywords1.InList(s)) {
+ sc.ChangeState(SCE_SQL_WORD);
+ } else if (keywords2.InList(s)) {
+ sc.ChangeState(SCE_SQL_WORD2);
+ } else if (kw_sqlplus.InListAbbreviated(s, '~')) {
+ sc.ChangeState(SCE_SQL_SQLPLUS);
+ if (strncmp(s, "rem", 3) == 0) {
+ nextState = SCE_SQL_SQLPLUS_COMMENT;
+ } else if (strncmp(s, "pro", 3) == 0) {
+ nextState = SCE_SQL_SQLPLUS_PROMPT;
+ }
+ } else if (kw_user1.InList(s)) {
+ sc.ChangeState(SCE_SQL_USER1);
+ } else if (kw_user2.InList(s)) {
+ sc.ChangeState(SCE_SQL_USER2);
+ } else if (kw_user3.InList(s)) {
+ sc.ChangeState(SCE_SQL_USER3);
+ } else if (kw_user4.InList(s)) {
+ sc.ChangeState(SCE_SQL_USER4);
+ }
+ sc.SetState(nextState);
+ }
+ break;
+ case SCE_SQL_QUOTEDIDENTIFIER:
+ if (sc.ch == 0x60) {
+ if (sc.chNext == 0x60) {
+ sc.Forward(); // Ignore it
+ } else {
+ sc.ForwardSetState(SCE_SQL_DEFAULT);
+ }
+ }
+ break;
+ case SCE_SQL_COMMENT:
+ if (sc.Match('*', '/')) {
+ sc.Forward();
+ sc.ForwardSetState(SCE_SQL_DEFAULT);
+ }
+ break;
+ case SCE_SQL_COMMENTDOC:
+ if (sc.Match('*', '/')) {
+ sc.Forward();
+ sc.ForwardSetState(SCE_SQL_DEFAULT);
+ } else if (sc.ch == '@' || sc.ch == '\\') { // Doxygen support
+ // Verify that we have the conditions to mark a comment-doc-keyword
+ if ((IsASpace(sc.chPrev) || sc.chPrev == '*') && (!IsASpace(sc.chNext))) {
+ styleBeforeDCKeyword = SCE_SQL_COMMENTDOC;
+ sc.SetState(SCE_SQL_COMMENTDOCKEYWORD);
+ }
+ }
+ break;
+ case SCE_SQL_COMMENTLINE:
+ case SCE_SQL_COMMENTLINEDOC:
+ case SCE_SQL_SQLPLUS_COMMENT:
+ case SCE_SQL_SQLPLUS_PROMPT:
+ if (sc.atLineStart) {
+ sc.SetState(SCE_SQL_DEFAULT);
+ }
+ break;
+ case SCE_SQL_COMMENTDOCKEYWORD:
+ if ((styleBeforeDCKeyword == SCE_SQL_COMMENTDOC) && sc.Match('*', '/')) {
+ sc.ChangeState(SCE_SQL_COMMENTDOCKEYWORDERROR);
+ sc.Forward();
+ sc.ForwardSetState(SCE_SQL_DEFAULT);
+ } else if (!IsADoxygenChar(sc.ch)) {
+ char s[100];
+ sc.GetCurrentLowered(s, sizeof(s));
+ if (!isspace(sc.ch) || !kw_pldoc.InList(s + 1)) {
+ sc.ChangeState(SCE_SQL_COMMENTDOCKEYWORDERROR);
+ }
+ sc.SetState(styleBeforeDCKeyword);
+ }
+ break;
+ case SCE_SQL_CHARACTER:
+ if (sqlBackslashEscapes && sc.ch == '\\') {
+ sc.Forward();
+ } else if (sc.ch == '\'') {
+ if (sc.chNext == '\"') {
+ sc.Forward();
+ } else {
+ sc.ForwardSetState(SCE_SQL_DEFAULT);
+ }
+ }
+ break;
+ case SCE_SQL_STRING:
+ if (sc.ch == '\\') {
+ // Escape sequence
+ sc.Forward();
+ } else if (sc.ch == '\"') {
+ if (sc.chNext == '\"') {
+ sc.Forward();
+ } else {
+ sc.ForwardSetState(SCE_SQL_DEFAULT);
+ }
+ }
+ break;
+ }
+
+ // Determine if a new state should be entered.
+ if (sc.state == SCE_SQL_DEFAULT) {
+ if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
+ sc.SetState(SCE_SQL_NUMBER);
+ } else if (IsAWordStart(sc.ch)) {
+ sc.SetState(SCE_SQL_IDENTIFIER);
+ } else if (sc.ch == 0x60 && sqlBackticksIdentifier) {
+ sc.SetState(SCE_SQL_QUOTEDIDENTIFIER);
+ } else if (sc.Match('/', '*')) {
+ if (sc.Match("/**") || sc.Match("/*!")) { // Support of Doxygen doc. style
+ sc.SetState(SCE_SQL_COMMENTDOC);
+ } else {
+ sc.SetState(SCE_SQL_COMMENT);
+ }
+ sc.Forward(); // Eat the * so it isn't used for the end of the comment
+ } else if (sc.Match('-', '-')) {
+ // MySQL requires a space or control char after --
+ // http://dev.mysql.com/doc/mysql/en/ansi-diff-comments.html
+ // Perhaps we should enforce that with proper property:
+//~ } else if (sc.Match("-- ")) {
+ sc.SetState(SCE_SQL_COMMENTLINE);
+ } else if (sc.ch == '#') {
+ sc.SetState(SCE_SQL_COMMENTLINEDOC);
+ } else if (sc.ch == '\'') {
+ sc.SetState(SCE_SQL_CHARACTER);
+ } else if (sc.ch == '\"') {
+ sc.SetState(SCE_SQL_STRING);
+ } else if (isoperator(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_SQL_OPERATOR);
+ }
+ }
+ }
+ sc.Complete();
+}
+
+static bool IsStreamCommentStyle(int style) {
+ return style == SCE_SQL_COMMENT ||
+ style == SCE_SQL_COMMENTDOC ||
+ style == SCE_SQL_COMMENTDOCKEYWORD ||
+ style == SCE_SQL_COMMENTDOCKEYWORDERROR;
+}
+
+// Store both the current line's fold level and the next lines in the
+// level store to make it easy to pick up with each increment.
+static void FoldSQLDoc(unsigned int startPos, int length, int initStyle,
+ WordList *[], Accessor &styler) {
+ bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ bool foldOnlyBegin = styler.GetPropertyInt("fold.sql.only.begin", 0) != 0;
+
+ unsigned int endPos = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelCurrent = SC_FOLDLEVELBASE;
+ if (lineCurrent > 0) {
+ levelCurrent = styler.LevelAt(lineCurrent - 1) >> 16;
+ }
+ int levelNext = levelCurrent;
+ char chNext = styler[startPos];
+ int styleNext = styler.StyleAt(startPos);
+ int style = initStyle;
+ bool endFound = false;
+ for (unsigned int i = startPos; i < endPos; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ int stylePrev = style;
+ style = styleNext;
+ styleNext = styler.StyleAt(i + 1);
+ bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
+ if (foldComment && IsStreamCommentStyle(style)) {
+ if (!IsStreamCommentStyle(stylePrev)) {
+ levelNext++;
+ } else if (!IsStreamCommentStyle(styleNext) && !atEOL) {
+ // Comments don't end at end of line and the next character may be unstyled.
+ levelNext--;
+ }
+ }
+ if (foldComment && (style == SCE_SQL_COMMENTLINE)) {
+ // MySQL needs -- comments to be followed by space or control char
+ if ((ch == '-') && (chNext == '-')) {
+ char chNext2 = styler.SafeGetCharAt(i + 2);
+ char chNext3 = styler.SafeGetCharAt(i + 3);
+ if (chNext2 == '{' || chNext3 == '{') {
+ levelNext++;
+ } else if (chNext2 == '}' || chNext3 == '}') {
+ levelNext--;
+ }
+ }
+ }
+ if (style == SCE_SQL_OPERATOR) {
+ if (ch == '(') {
+ levelNext++;
+ } else if (ch == ')') {
+ levelNext--;
+ }
+ }
+ // If new keyword (cannot trigger on elseif or nullif, does less tests)
+ if (style == SCE_SQL_WORD && stylePrev != SCE_SQL_WORD) {
+ const int MAX_KW_LEN = 6; // Maximum length of folding keywords
+ char s[MAX_KW_LEN + 2];
+ unsigned int j = 0;
+ for (; j < MAX_KW_LEN + 1; j++) {
+ if (!iswordchar(styler[i + j])) {
+ break;
+ }
+ s[j] = static_cast<char>(tolower(styler[i + j]));
+ }
+ if (j == MAX_KW_LEN + 1) {
+ // Keyword too long, don't test it
+ s[0] = '\0';
+ } else {
+ s[j] = '\0';
+ }
+ if ((!foldOnlyBegin) && (strcmp(s, "if") == 0 || strcmp(s, "loop") == 0)) {
+ if (endFound) {
+ // ignore
+ endFound = false;
+ } else {
+ levelNext++;
+ }
+ } else if (strcmp(s, "begin") == 0) {
+ levelNext++;
+ } else if (strcmp(s, "end") == 0 ||
+ // DROP TABLE IF EXISTS or CREATE TABLE IF NOT EXISTS
+ strcmp(s, "exists") == 0) {
+ endFound = true;
+ levelNext--;
+ if (levelNext < SC_FOLDLEVELBASE) {
+ levelNext = SC_FOLDLEVELBASE;
+ }
+ }
+ }
+ if (atEOL) {
+ int levelUse = levelCurrent;
+ int lev = levelUse | levelNext << 16;
+ if (visibleChars == 0 && foldCompact)
+ lev |= SC_FOLDLEVELWHITEFLAG;
+ if (levelUse < levelNext)
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ if (lev != styler.LevelAt(lineCurrent)) {
+ styler.SetLevel(lineCurrent, lev);
+ }
+ lineCurrent++;
+ levelCurrent = levelNext;
+ visibleChars = 0;
+ endFound = false;
+ }
+ if (!isspacechar(ch)) {
+ visibleChars++;
+ }
+ }
+}
+
+static const char * const sqlWordListDesc[] = {
+ "Keywords",
+ "Database Objects",
+ "PLDoc",
+ "SQL*Plus",
+ "User Keywords 1",
+ "User Keywords 2",
+ "User Keywords 3",
+ "User Keywords 4",
+ 0
+};
+
+LexerModule lmSQL(SCLEX_SQL, ColouriseSQLDoc, "sql", FoldSQLDoc, sqlWordListDesc);
diff --git a/src/LexScriptol.cpp b/src/LexScriptol.cpp
new file mode 100755
index 0000000..faaa2d4
--- /dev/null
+++ b/src/LexScriptol.cpp
@@ -0,0 +1,404 @@
+// Scintilla source code edit control
+/** @file LexScriptol.cxx
+ ** Lexer for Scriptol.
+ **/
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+static void ClassifyWordSol(unsigned int start, unsigned int end, WordList &keywords, Accessor &styler, char *prevWord)
+{
+ char s[100];
+ bool wordIsNumber = isdigit(styler[start]) != 0;
+ for (unsigned int i = 0; i < end - start + 1 && i < 30; i++)
+ {
+ s[i] = styler[start + i];
+ s[i + 1] = '\0';
+ }
+ char chAttr = SCE_SCRIPTOL_IDENTIFIER;
+ if (0 == strcmp(prevWord, "class")) chAttr = SCE_SCRIPTOL_CLASSNAME;
+ else if (wordIsNumber) chAttr = SCE_SCRIPTOL_NUMBER;
+ else if (keywords.InList(s)) chAttr = SCE_SCRIPTOL_KEYWORD;
+ else for (unsigned int i = 0; i < end - start + 1; i++) // test dotted idents
+ {
+ if (styler[start + i] == '.')
+ {
+ styler.ColourTo(start + i - 1, chAttr);
+ styler.ColourTo(start + i, SCE_SCRIPTOL_OPERATOR);
+ }
+ }
+ styler.ColourTo(end, chAttr);
+ strcpy(prevWord, s);
+}
+
+static bool IsSolComment(Accessor &styler, int pos, int len)
+{
+ char c;
+ if(len > 0)
+ {
+ c = styler[pos];
+ if(c == '`') return true;
+ if(len > 1)
+ {
+ if(c == '/')
+ {
+ c = styler[pos + 1];
+ if(c == '/') return true;
+ if(c == '*') return true;
+ }
+ }
+ }
+ return false;
+}
+
+static bool IsSolStringStart(char ch)
+{
+ if (ch == '\'' || ch == '"') return true;
+ return false;
+}
+
+static bool IsSolWordStart(char ch)
+{
+ return (iswordchar(ch) && !IsSolStringStart(ch));
+}
+
+
+static int GetSolStringState(Accessor &styler, int i, int *nextIndex)
+{
+ char ch = styler.SafeGetCharAt(i);
+ char chNext = styler.SafeGetCharAt(i + 1);
+
+ if (ch != '\"' && ch != '\'')
+ {
+ *nextIndex = i + 1;
+ return SCE_SCRIPTOL_DEFAULT;
+ }
+ // ch is either single or double quotes in string
+ // code below seem non-sense but is here for future extensions
+ if (ch == chNext && ch == styler.SafeGetCharAt(i + 2))
+ {
+ *nextIndex = i + 3;
+ if(ch == '\"') return SCE_SCRIPTOL_TRIPLE;
+ if(ch == '\'') return SCE_SCRIPTOL_TRIPLE;
+ return SCE_SCRIPTOL_STRING;
+ }
+ else
+ {
+ *nextIndex = i + 1;
+ if (ch == '"') return SCE_SCRIPTOL_STRING;
+ else return SCE_SCRIPTOL_STRING;
+ }
+}
+
+
+static void ColouriseSolDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler)
+ {
+
+ int lengthDoc = startPos + length;
+ char stringType = '\"';
+
+ if (startPos > 0)
+ {
+ int lineCurrent = styler.GetLine(startPos);
+ if (lineCurrent > 0)
+ {
+ startPos = styler.LineStart(lineCurrent-1);
+ if (startPos == 0) initStyle = SCE_SCRIPTOL_DEFAULT;
+ else initStyle = styler.StyleAt(startPos-1);
+ }
+ }
+
+ styler.StartAt(startPos, 127);
+
+ WordList &keywords = *keywordlists[0];
+
+ int whingeLevel = styler.GetPropertyInt("tab.timmy.whinge.level");
+ char prevWord[200];
+ prevWord[0] = '\0';
+ if (length == 0) return;
+
+ int state = initStyle & 31;
+
+ int nextIndex = 0;
+ char chPrev = ' ';
+ char chPrev2 = ' ';
+ char chNext = styler[startPos];
+ styler.StartSegment(startPos);
+ bool atStartLine = true;
+ int spaceFlags = 0;
+ for (int i = startPos; i < lengthDoc; i++)
+ {
+
+ if (atStartLine)
+ {
+ char chBad = static_cast<char>(64);
+ char chGood = static_cast<char>(0);
+ char chFlags = chGood;
+
+ if (whingeLevel == 1)
+ {
+ chFlags = (spaceFlags & wsInconsistent) ? chBad : chGood;
+ }
+ else if (whingeLevel == 2)
+ {
+ chFlags = (spaceFlags & wsSpaceTab) ? chBad : chGood;
+ }
+ else if (whingeLevel == 3)
+ {
+ chFlags = (spaceFlags & wsSpace) ? chBad : chGood;
+ }
+ else if (whingeLevel == 4)
+ {
+ chFlags = (spaceFlags & wsTab) ? chBad : chGood;
+ }
+ styler.SetFlags(chFlags, static_cast<char>(state));
+ atStartLine = false;
+ }
+
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+
+ if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == lengthDoc))
+ {
+ if ((state == SCE_SCRIPTOL_DEFAULT) ||
+ (state == SCE_SCRIPTOL_TRIPLE) ||
+ (state == SCE_SCRIPTOL_COMMENTBLOCK))
+ {
+ styler.ColourTo(i, state);
+ }
+ atStartLine = true;
+ }
+
+ if (styler.IsLeadByte(ch))
+ {
+ chNext = styler.SafeGetCharAt(i + 2);
+ chPrev = ' ';
+ chPrev2 = ' ';
+ i += 1;
+ continue;
+ }
+
+ if (state == SCE_SCRIPTOL_STRINGEOL)
+ {
+ if (ch != '\r' && ch != '\n')
+ {
+ styler.ColourTo(i - 1, state);
+ state = SCE_SCRIPTOL_DEFAULT;
+ }
+ }
+
+ if (state == SCE_SCRIPTOL_DEFAULT)
+ {
+ if (IsSolWordStart(ch))
+ {
+ styler.ColourTo(i - 1, state);
+ state = SCE_SCRIPTOL_KEYWORD;
+ }
+ else if (ch == '`')
+ {
+ styler.ColourTo(i - 1, state);
+ state = SCE_SCRIPTOL_COMMENTLINE;
+ }
+ else if (ch == '/')
+ {
+ styler.ColourTo(i - 1, state);
+ if(chNext == '/') state = SCE_SCRIPTOL_CSTYLE;
+ if(chNext == '*') state = SCE_SCRIPTOL_COMMENTBLOCK;
+ }
+
+ else if (IsSolStringStart(ch))
+ {
+ styler.ColourTo(i - 1, state);
+ state = GetSolStringState(styler, i, &nextIndex);
+ if(state == SCE_SCRIPTOL_STRING)
+ {
+ stringType = ch;
+ }
+ if (nextIndex != i + 1)
+ {
+ i = nextIndex - 1;
+ ch = ' ';
+ chPrev = ' ';
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ }
+ else if (isoperator(ch))
+ {
+ styler.ColourTo(i - 1, state);
+ styler.ColourTo(i, SCE_SCRIPTOL_OPERATOR);
+ }
+ }
+ else if (state == SCE_SCRIPTOL_KEYWORD)
+ {
+ if (!iswordchar(ch))
+ {
+ ClassifyWordSol(styler.GetStartSegment(), i - 1, keywords, styler, prevWord);
+ state = SCE_SCRIPTOL_DEFAULT;
+ if (ch == '`')
+ {
+ state = chNext == '`' ? SCE_SCRIPTOL_PERSISTENT : SCE_SCRIPTOL_COMMENTLINE;
+ }
+ else if (IsSolStringStart(ch))
+ {
+ styler.ColourTo(i - 1, state);
+ state = GetSolStringState(styler, i, &nextIndex);
+ if (nextIndex != i + 1)
+ {
+ i = nextIndex - 1;
+ ch = ' ';
+ chPrev = ' ';
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ }
+ else if (isoperator(ch))
+ {
+ styler.ColourTo(i, SCE_SCRIPTOL_OPERATOR);
+ }
+ }
+ }
+ else
+ {
+ if (state == SCE_SCRIPTOL_COMMENTLINE ||
+ state == SCE_SCRIPTOL_PERSISTENT ||
+ state == SCE_SCRIPTOL_CSTYLE)
+ {
+ if (ch == '\r' || ch == '\n')
+ {
+ styler.ColourTo(i - 1, state);
+ state = SCE_SCRIPTOL_DEFAULT;
+ }
+ }
+ else if(state == SCE_SCRIPTOL_COMMENTBLOCK)
+ {
+ if(chPrev == '*' && ch == '/')
+ {
+ styler.ColourTo(i, state);
+ state = SCE_SCRIPTOL_DEFAULT;
+ }
+ }
+ else if ((state == SCE_SCRIPTOL_STRING) ||
+ (state == SCE_SCRIPTOL_CHARACTER))
+ {
+ if ((ch == '\r' || ch == '\n') && (chPrev != '\\'))
+ {
+ styler.ColourTo(i - 1, state);
+ state = SCE_SCRIPTOL_STRINGEOL;
+ }
+ else if (ch == '\\')
+ {
+ if (chNext == '\"' || chNext == '\'' || chNext == '\\')
+ {
+ i++;
+ ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ }
+ }
+ else if ((ch == '\"') || (ch == '\''))
+ {
+ // must match the entered quote type
+ if(ch == stringType)
+ {
+ styler.ColourTo(i, state);
+ state = SCE_SCRIPTOL_DEFAULT;
+ }
+ }
+ }
+ else if (state == SCE_SCRIPTOL_TRIPLE)
+ {
+ if ((ch == '\'' && chPrev == '\'' && chPrev2 == '\'') ||
+ (ch == '\"' && chPrev == '\"' && chPrev2 == '\"'))
+ {
+ styler.ColourTo(i, state);
+ state = SCE_SCRIPTOL_DEFAULT;
+ }
+ }
+
+ }
+ chPrev2 = chPrev;
+ chPrev = ch;
+ }
+ if (state == SCE_SCRIPTOL_KEYWORD)
+ {
+ ClassifyWordSol(styler.GetStartSegment(),
+ lengthDoc-1, keywords, styler, prevWord);
+ }
+ else
+ {
+ styler.ColourTo(lengthDoc-1, state);
+ }
+}
+
+static void FoldSolDoc(unsigned int startPos, int length, int initStyle,
+ WordList *[], Accessor &styler)
+ {
+ int lengthDoc = startPos + length;
+
+ int lineCurrent = styler.GetLine(startPos);
+ if (startPos > 0)
+ {
+ if (lineCurrent > 0)
+ {
+ lineCurrent--;
+ startPos = styler.LineStart(lineCurrent);
+ if (startPos == 0)
+ initStyle = SCE_SCRIPTOL_DEFAULT;
+ else
+ initStyle = styler.StyleAt(startPos-1);
+ }
+ }
+ int state = initStyle & 31;
+ int spaceFlags = 0;
+ int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, IsSolComment);
+ if ((state == SCE_SCRIPTOL_TRIPLE))
+ indentCurrent |= SC_FOLDLEVELWHITEFLAG;
+ char chNext = styler[startPos];
+ for (int i = startPos; i < lengthDoc; i++)
+ {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ int style = styler.StyleAt(i) & 31;
+
+ if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == lengthDoc))
+ {
+ int lev = indentCurrent;
+ int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags, IsSolComment);
+ if (style == SCE_SCRIPTOL_TRIPLE)
+ indentNext |= SC_FOLDLEVELWHITEFLAG;
+ if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG))
+ {
+ // Only non whitespace lines can be headers
+ if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK))
+ {
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ }
+ else if (indentNext & SC_FOLDLEVELWHITEFLAG)
+ {
+ // Line after is blank so check the next - maybe should continue further?
+ int spaceFlags2 = 0;
+ int indentNext2 = styler.IndentAmount(lineCurrent + 2, &spaceFlags2, IsSolComment);
+ if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext2 & SC_FOLDLEVELNUMBERMASK))
+ {
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ }
+ }
+ }
+ indentCurrent = indentNext;
+ styler.SetLevel(lineCurrent, lev);
+ lineCurrent++;
+ }
+ }
+}
+
+LexerModule lmScriptol(SCLEX_SCRIPTOL, ColouriseSolDoc, "scriptol", FoldSolDoc);
diff --git a/src/LexSmalltalk.cpp b/src/LexSmalltalk.cpp
new file mode 100644
index 0000000..6f43ec3
--- /dev/null
+++ b/src/LexSmalltalk.cpp
@@ -0,0 +1,317 @@
+// Scintilla source code edit control
+/** @file LexSmalltalk.cxx
+ ** Lexer for Smalltalk language.
+ ** Written by Sergey Philippov, sphilippov-at-gmail-dot-com
+ **/
+// Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+/*
+| lexTable classificationBlock charClasses |
+charClasses := #(#DecDigit #Letter #Special #Upper #BinSel).
+lexTable := ByteArray new: 128.
+classificationBlock := [ :charClass :chars |
+ | flag |
+ flag := 1 bitShift: (charClasses indexOf: charClass) - 1.
+ chars do: [ :char | lexTable at: char codePoint + 1 put: ((lexTable at: char codePoint + 1) bitOr: flag)]].
+
+classificationBlock
+ value: #DecDigit value: '0123456789';
+ value: #Letter value: '_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ value: #Special value: '()[]{};.^:';
+ value: #BinSel value: '~@%&*-+=|\/,<>?!';
+ value: #Upper value: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
+
+((String new: 500) streamContents: [ :stream |
+ stream crLf; nextPutAll: 'static int ClassificationTable[256] = {'.
+ lexTable keysAndValuesDo: [ :index :value |
+ ((index - 1) rem: 16) == 0 ifTrue: [
+ stream crLf; tab]
+ ifFalse: [
+ stream space].
+ stream print: value.
+ index ~= 256 ifTrue: [
+ stream nextPut: $,]].
+ stream crLf; nextPutAll: '};'; crLf.
+
+ charClasses keysAndValuesDo: [ :index :name |
+ stream
+ crLf;
+ nextPutAll: (
+ ('static inline bool is<1s>(int ch) {return (ch > 0) && (ch %< 0x80) && ((ClassificationTable[ch] & <2p>) != 0);}')
+ expandMacrosWith: name with: (1 bitShift: (index - 1)))
+ ]]) edit
+*/
+
+// autogenerated {{{{
+
+static int ClassificationTable[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 16, 0, 0, 0, 16, 16, 0, 4, 4, 16, 16, 16, 16, 4, 16,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 16, 16, 16, 16,
+ 16, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 4, 16, 4, 4, 2,
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 16, 4, 16, 0,
+};
+
+static inline bool isDecDigit(int ch) {return (ch > 0) && (ch < 0x80) && ((ClassificationTable[ch] & 1) != 0);}
+static inline bool isLetter(int ch) {return (ch > 0) && (ch < 0x80) && ((ClassificationTable[ch] & 2) != 0);}
+static inline bool isSpecial(int ch) {return (ch > 0) && (ch < 0x80) && ((ClassificationTable[ch] & 4) != 0);}
+static inline bool isUpper(int ch) {return (ch > 0) && (ch < 0x80) && ((ClassificationTable[ch] & 8) != 0);}
+static inline bool isBinSel(int ch) {return (ch > 0) && (ch < 0x80) && ((ClassificationTable[ch] & 16) != 0);}
+// autogenerated }}}}
+
+static inline bool isAlphaNumeric(int ch) {
+ return isDecDigit(ch) || isLetter(ch);
+}
+
+static inline bool isDigitOfRadix(int ch, int radix)
+{
+ if (isDecDigit(ch))
+ return (ch - '0') < radix;
+ else if (!isUpper(ch))
+ return false;
+ else
+ return (ch - 'A' + 10) < radix;
+}
+
+static inline void skipComment(StyleContext& sc)
+{
+ while (sc.More() && sc.ch != '\"')
+ sc.Forward();
+}
+
+static inline void skipString(StyleContext& sc)
+{
+ while (sc.More()) {
+ if (sc.ch == '\'') {
+ if (sc.chNext != '\'')
+ return;
+ sc.Forward();
+ }
+ sc.Forward();
+ }
+}
+
+static void handleHash(StyleContext& sc)
+{
+ if (isSpecial(sc.chNext)) {
+ sc.SetState(SCE_ST_SPECIAL);
+ return;
+ }
+
+ sc.SetState(SCE_ST_SYMBOL);
+ sc.Forward();
+ if (sc.ch == '\'') {
+ sc.Forward();
+ skipString(sc);
+ }
+ else {
+ if (isLetter(sc.ch)) {
+ while (isAlphaNumeric(sc.chNext) || sc.chNext == ':')
+ sc.Forward();
+ }
+ else if (isBinSel(sc.ch)) {
+ while (isBinSel(sc.chNext))
+ sc.Forward();
+ }
+ }
+}
+
+static inline void handleSpecial(StyleContext& sc)
+{
+ if (sc.ch == ':' && sc.chNext == '=') {
+ sc.SetState(SCE_ST_ASSIGN);
+ sc.Forward();
+ }
+ else {
+ if (sc.ch == '^')
+ sc.SetState(SCE_ST_RETURN);
+ else
+ sc.SetState(SCE_ST_SPECIAL);
+ }
+}
+
+static inline void skipInt(StyleContext& sc, int radix)
+{
+ while (isDigitOfRadix(sc.chNext, radix))
+ sc.Forward();
+}
+
+static void handleNumeric(StyleContext& sc)
+{
+ char num[256];
+ int nl;
+ int radix;
+
+ sc.SetState(SCE_ST_NUMBER);
+ num[0] = static_cast<char>(sc.ch);
+ nl = 1;
+ while (isDecDigit(sc.chNext)) {
+ num[nl++] = static_cast<char>(sc.chNext);
+ sc.Forward();
+ if (nl+1 == sizeof(num)/sizeof(num[0])) // overrun check
+ break;
+ }
+ if (sc.chNext == 'r') {
+ num[nl] = 0;
+ if (num[0] == '-')
+ radix = atoi(num + 1);
+ else
+ radix = atoi(num);
+ sc.Forward();
+ if (sc.chNext == '-')
+ sc.Forward();
+ skipInt(sc, radix);
+ }
+ else
+ radix = 10;
+ if (sc.chNext != '.' || !isDigitOfRadix(sc.GetRelative(2), radix))
+ return;
+ sc.Forward();
+ skipInt(sc, radix);
+ if (sc.chNext == 's') {
+ // ScaledDecimal
+ sc.Forward();
+ while (isDecDigit(sc.chNext))
+ sc.Forward();
+ return;
+ }
+ else if (sc.chNext != 'e' && sc.chNext != 'd' && sc.chNext != 'q')
+ return;
+ sc.Forward();
+ if (sc.chNext == '+' || sc.chNext == '-')
+ sc.Forward();
+ skipInt(sc, radix);
+}
+
+static inline void handleBinSel(StyleContext& sc)
+{
+ sc.SetState(SCE_ST_BINARY);
+ while (isBinSel(sc.chNext))
+ sc.Forward();
+}
+
+static void handleLetter(StyleContext& sc, WordList* specialSelectorList)
+{
+ char ident[256];
+ int il;
+ int state;
+ bool doubleColonPresent;
+
+ sc.SetState(SCE_ST_DEFAULT);
+
+ ident[0] = static_cast<char>(sc.ch);
+ il = 1;
+ while (isAlphaNumeric(sc.chNext)) {
+ ident[il++] = static_cast<char>(sc.chNext);
+ sc.Forward();
+ if (il+2 == sizeof(ident)/sizeof(ident[0])) // overrun check
+ break;
+ }
+
+ if (sc.chNext == ':') {
+ doubleColonPresent = true;
+ ident[il++] = ':';
+ sc.Forward();
+ }
+ else
+ doubleColonPresent = false;
+ ident[il] = 0;
+
+ if (specialSelectorList->InList(ident))
+ state = SCE_ST_SPEC_SEL;
+ else if (doubleColonPresent)
+ state = SCE_ST_KWSEND;
+ else if (isUpper(ident[0]))
+ state = SCE_ST_GLOBAL;
+ else {
+ if (!strcmp(ident, "self"))
+ state = SCE_ST_SELF;
+ else if (!strcmp(ident, "super"))
+ state = SCE_ST_SUPER;
+ else if (!strcmp(ident, "nil"))
+ state = SCE_ST_NIL;
+ else if (!strcmp(ident, "true") || !strcmp(ident, "false"))
+ state = SCE_ST_BOOL;
+ else
+ state = SCE_ST_DEFAULT;
+ }
+
+ sc.ChangeState(state);
+}
+
+static void colorizeSmalltalkDoc(unsigned int startPos, int length, int initStyle, WordList *wordLists[], Accessor &styler)
+{
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ if (initStyle == SCE_ST_COMMENT) {
+ skipComment(sc);
+ if (sc.More())
+ sc.Forward();
+ }
+ else if (initStyle == SCE_ST_STRING) {
+ skipString(sc);
+ if (sc.More())
+ sc.Forward();
+ }
+
+ for (; sc.More(); sc.Forward()) {
+ int ch;
+
+ ch = sc.ch;
+ if (ch == '\"') {
+ sc.SetState(SCE_ST_COMMENT);
+ sc.Forward();
+ skipComment(sc);
+ }
+ else if (ch == '\'') {
+ sc.SetState(SCE_ST_STRING);
+ sc.Forward();
+ skipString(sc);
+ }
+ else if (ch == '#')
+ handleHash(sc);
+ else if (ch == '$') {
+ sc.SetState(SCE_ST_CHARACTER);
+ sc.Forward();
+ }
+ else if (isSpecial(ch))
+ handleSpecial(sc);
+ else if (isDecDigit(ch))
+ handleNumeric(sc);
+ else if (isLetter(ch))
+ handleLetter(sc, wordLists[0]);
+ else if (isBinSel(ch)) {
+ if (ch == '-' && isDecDigit(sc.chNext))
+ handleNumeric(sc);
+ else
+ handleBinSel(sc);
+ }
+ else
+ sc.SetState(SCE_ST_DEFAULT);
+ }
+ sc.Complete();
+}
+
+static const char* const smalltalkWordListDesc[] = {
+ "Special selectors",
+ 0
+};
+
+LexerModule lmSmalltalk(SCLEX_SMALLTALK, colorizeSmalltalkDoc, "smalltalk", NULL, smalltalkWordListDesc);
diff --git a/src/LexSpecman.cpp b/src/LexSpecman.cpp
new file mode 100755
index 0000000..bf5d639
--- /dev/null
+++ b/src/LexSpecman.cpp
@@ -0,0 +1,286 @@
+// Scintilla source code edit control
+/** @file LexSpecman.cxx
+ ** Lexer for Specman E language.
+ ** Written by Avi Yegudin, based on C++ lexer by Neil Hodgson
+ **/
+// Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+
+static inline bool IsAWordChar(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_' || ch == '\'');
+}
+
+static inline bool IsANumberChar(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '\'');
+}
+
+static inline bool IsAWordStart(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '`');
+}
+
+static void ColouriseSpecmanDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+ Accessor &styler, bool caseSensitive) {
+
+ WordList &keywords = *keywordlists[0];
+ WordList &keywords2 = *keywordlists[1];
+ WordList &keywords3 = *keywordlists[2];
+ WordList &keywords4 = *keywordlists[3];
+
+ // Do not leak onto next line
+ if (initStyle == SCE_SN_STRINGEOL)
+ initStyle = SCE_SN_CODE;
+
+ int visibleChars = 0;
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ for (; sc.More(); sc.Forward()) {
+
+ if (sc.atLineStart && (sc.state == SCE_SN_STRING)) {
+ // Prevent SCE_SN_STRINGEOL from leaking back to previous line
+ sc.SetState(SCE_SN_STRING);
+ }
+
+ // Handle line continuation generically.
+ if (sc.ch == '\\') {
+ if (sc.chNext == '\n' || sc.chNext == '\r') {
+ sc.Forward();
+ if (sc.ch == '\r' && sc.chNext == '\n') {
+ sc.Forward();
+ }
+ continue;
+ }
+ }
+
+ // Determine if the current state should terminate.
+ if (sc.state == SCE_SN_OPERATOR) {
+ sc.SetState(SCE_SN_CODE);
+ } else if (sc.state == SCE_SN_NUMBER) {
+ if (!IsANumberChar(sc.ch)) {
+ sc.SetState(SCE_SN_CODE);
+ }
+ } else if (sc.state == SCE_SN_IDENTIFIER) {
+ if (!IsAWordChar(sc.ch) || (sc.ch == '.')) {
+ char s[100];
+ if (caseSensitive) {
+ sc.GetCurrent(s, sizeof(s));
+ } else {
+ sc.GetCurrentLowered(s, sizeof(s));
+ }
+ if (keywords.InList(s)) {
+ sc.ChangeState(SCE_SN_WORD);
+ } else if (keywords2.InList(s)) {
+ sc.ChangeState(SCE_SN_WORD2);
+ } else if (keywords3.InList(s)) {
+ sc.ChangeState(SCE_SN_WORD3);
+ } else if (keywords4.InList(s)) {
+ sc.ChangeState(SCE_SN_USER);
+ }
+ sc.SetState(SCE_SN_CODE);
+ }
+ } else if (sc.state == SCE_SN_PREPROCESSOR) {
+ if (IsASpace(sc.ch)) {
+ sc.SetState(SCE_SN_CODE);
+ }
+ } else if (sc.state == SCE_SN_DEFAULT) {
+ if (sc.Match('<', '\'')) {
+ sc.Forward();
+ sc.ForwardSetState(SCE_SN_CODE);
+ }
+ } else if (sc.state == SCE_SN_COMMENTLINE || sc.state == SCE_SN_COMMENTLINEBANG) {
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_SN_CODE);
+ visibleChars = 0;
+ }
+ } else if (sc.state == SCE_SN_STRING) {
+ if (sc.ch == '\\') {
+ if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
+ sc.Forward();
+ }
+ } else if (sc.ch == '\"') {
+ sc.ForwardSetState(SCE_SN_CODE);
+ } else if (sc.atLineEnd) {
+ sc.ChangeState(SCE_SN_STRINGEOL);
+ sc.ForwardSetState(SCE_SN_CODE);
+ visibleChars = 0;
+ }
+ } else if (sc.state == SCE_SN_SIGNAL) {
+ if (sc.atLineEnd) {
+ sc.ChangeState(SCE_SN_STRINGEOL);
+ sc.ForwardSetState(SCE_SN_CODE);
+ visibleChars = 0;
+ } else if (sc.ch == '\\') {
+ if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
+ sc.Forward();
+ }
+ } else if (sc.ch == '\'') {
+ sc.ForwardSetState(SCE_SN_CODE);
+ }
+ } else if (sc.state == SCE_SN_REGEXTAG) {
+ if (!IsADigit(sc.ch)) {
+ sc.SetState(SCE_SN_CODE);
+ }
+ }
+
+ // Determine if a new state should be entered.
+ if (sc.state == SCE_SN_CODE) {
+ if (sc.ch == '$' && IsADigit(sc.chNext)) {
+ sc.SetState(SCE_SN_REGEXTAG);
+ sc.Forward();
+ } else if (IsADigit(sc.ch)) {
+ sc.SetState(SCE_SN_NUMBER);
+ } else if (IsAWordStart(sc.ch)) {
+ sc.SetState(SCE_SN_IDENTIFIER);
+ } else if (sc.Match('\'', '>')) {
+ sc.SetState(SCE_SN_DEFAULT);
+ sc.Forward(); // Eat the * so it isn't used for the end of the comment
+ } else if (sc.Match('/', '/')) {
+ if (sc.Match("//!")) // Nice to have a different comment style
+ sc.SetState(SCE_SN_COMMENTLINEBANG);
+ else
+ sc.SetState(SCE_SN_COMMENTLINE);
+ } else if (sc.Match('-', '-')) {
+ if (sc.Match("--!")) // Nice to have a different comment style
+ sc.SetState(SCE_SN_COMMENTLINEBANG);
+ else
+ sc.SetState(SCE_SN_COMMENTLINE);
+ } else if (sc.ch == '\"') {
+ sc.SetState(SCE_SN_STRING);
+ } else if (sc.ch == '\'') {
+ sc.SetState(SCE_SN_SIGNAL);
+ } else if (sc.ch == '#' && visibleChars == 0) {
+ // Preprocessor commands are alone on their line
+ sc.SetState(SCE_SN_PREPROCESSOR);
+ // Skip whitespace between # and preprocessor word
+ do {
+ sc.Forward();
+ } while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_SN_CODE);
+ }
+ } else if (isoperator(static_cast<char>(sc.ch)) || sc.ch == '@') {
+ sc.SetState(SCE_SN_OPERATOR);
+ }
+ }
+
+ if (sc.atLineEnd) {
+ // Reset states to begining of colourise so no surprises
+ // if different sets of lines lexed.
+ visibleChars = 0;
+ }
+ if (!IsASpace(sc.ch)) {
+ visibleChars++;
+ }
+ }
+ sc.Complete();
+}
+
+// Store both the current line's fold level and the next lines in the
+// level store to make it easy to pick up with each increment
+// and to make it possible to fiddle the current level for "} else {".
+static void FoldNoBoxSpecmanDoc(unsigned int startPos, int length, int,
+ Accessor &styler) {
+ bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0;
+ unsigned int endPos = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelCurrent = SC_FOLDLEVELBASE;
+ if (lineCurrent > 0)
+ levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
+ int levelMinCurrent = levelCurrent;
+ int levelNext = levelCurrent;
+ char chNext = styler[startPos];
+ int styleNext = styler.StyleAt(startPos);
+ int style;
+ for (unsigned int i = startPos; i < endPos; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ //int stylePrev = style;
+ style = styleNext;
+ styleNext = styler.StyleAt(i + 1);
+ bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
+ if (foldComment && (style == SCE_SN_COMMENTLINE)) {
+ if (((ch == '/') && (chNext == '/')) ||
+ ((ch == '-') && (chNext == '-'))) {
+ char chNext2 = styler.SafeGetCharAt(i + 2);
+ if (chNext2 == '{') {
+ levelNext++;
+ } else if (chNext2 == '}') {
+ levelNext--;
+ }
+ }
+ }
+ if (style == SCE_SN_OPERATOR) {
+ if (ch == '{') {
+ // Measure the minimum before a '{' to allow
+ // folding on "} else {"
+ if (levelMinCurrent > levelNext) {
+ levelMinCurrent = levelNext;
+ }
+ levelNext++;
+ } else if (ch == '}') {
+ levelNext--;
+ }
+ }
+ if (atEOL) {
+ int levelUse = levelCurrent;
+ if (foldAtElse) {
+ levelUse = levelMinCurrent;
+ }
+ int lev = levelUse | levelNext << 16;
+ if (visibleChars == 0 && foldCompact)
+ lev |= SC_FOLDLEVELWHITEFLAG;
+ if (levelUse < levelNext)
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ if (lev != styler.LevelAt(lineCurrent)) {
+ styler.SetLevel(lineCurrent, lev);
+ }
+ lineCurrent++;
+ levelCurrent = levelNext;
+ levelMinCurrent = levelCurrent;
+ visibleChars = 0;
+ }
+ if (!isspacechar(ch))
+ visibleChars++;
+ }
+}
+
+static void FoldSpecmanDoc(unsigned int startPos, int length, int initStyle, WordList *[],
+ Accessor &styler) {
+ FoldNoBoxSpecmanDoc(startPos, length, initStyle, styler);
+}
+
+static const char * const specmanWordLists[] = {
+ "Primary keywords and identifiers",
+ "Secondary keywords and identifiers",
+ "Sequence keywords and identifiers",
+ "User defined keywords and identifiers",
+ "Unused",
+ 0,
+ };
+
+static void ColouriseSpecmanDocSensitive(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+ Accessor &styler) {
+ ColouriseSpecmanDoc(startPos, length, initStyle, keywordlists, styler, true);
+}
+
+
+LexerModule lmSpecman(SCLEX_SPECMAN, ColouriseSpecmanDocSensitive, "specman", FoldSpecmanDoc, specmanWordLists);
diff --git a/src/LexSpice.cpp b/src/LexSpice.cpp
new file mode 100644
index 0000000..8a1683f
--- /dev/null
+++ b/src/LexSpice.cpp
@@ -0,0 +1,221 @@
+// Scintilla source code edit control
+/** @file LexSpice.cxx
+ ** Lexer for Spice
+ **/
+// Copyright 2006 by Fabien Proriol
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "Platform.h"
+
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "PropSet.h"
+#include "KeyWords.h"
+#include "SciLexer.h"
+#include "SString.h"
+
+/*
+ * Interface
+ */
+
+static void ColouriseDocument(
+ unsigned int startPos,
+ int length,
+ int initStyle,
+ WordList *keywordlists[],
+ Accessor &styler);
+
+static const char * const spiceWordListDesc[] = {
+ "Keywords", // SPICE command
+ "Keywords2", // SPICE functions
+ "Keywords3", // SPICE params
+ 0
+};
+
+LexerModule lmSpice(SCLEX_SPICE, ColouriseDocument, "spice", NULL, spiceWordListDesc);
+
+/*
+ * Implementation
+ */
+
+static void ColouriseComment(StyleContext& sc, bool& apostropheStartsAttribute);
+static void ColouriseDelimiter(StyleContext& sc, bool& apostropheStartsAttribute);
+static void ColouriseNumber(StyleContext& sc, bool& apostropheStartsAttribute);
+static void ColouriseWhiteSpace(StyleContext& sc, bool& apostropheStartsAttribute);
+static void ColouriseWord(StyleContext& sc, WordList& keywords, WordList& keywords2, WordList& keywords3, bool& apostropheStartsAttribute);
+
+static inline bool IsDelimiterCharacter(int ch);
+static inline bool IsNumberStartCharacter(int ch);
+static inline bool IsNumberCharacter(int ch);
+static inline bool IsSeparatorOrDelimiterCharacter(int ch);
+static inline bool IsWordStartCharacter(int ch);
+static inline bool IsWordCharacter(int ch);
+
+static void ColouriseComment(StyleContext& sc, bool&) {
+ sc.SetState(SCE_SPICE_COMMENTLINE);
+ while (!sc.atLineEnd) {
+ sc.Forward();
+ }
+}
+
+static void ColouriseDelimiter(StyleContext& sc, bool& apostropheStartsAttribute) {
+ apostropheStartsAttribute = sc.Match (')');
+ sc.SetState(SCE_SPICE_DELIMITER);
+ sc.ForwardSetState(SCE_SPICE_DEFAULT);
+}
+
+static void ColouriseNumber(StyleContext& sc, bool& apostropheStartsAttribute) {
+ apostropheStartsAttribute = true;
+ SString number;
+ sc.SetState(SCE_SPICE_NUMBER);
+ // Get all characters up to a delimiter or a separator, including points, but excluding
+ // double points (ranges).
+ while (!IsSeparatorOrDelimiterCharacter(sc.ch) || (sc.ch == '.' && sc.chNext != '.')) {
+ number += static_cast<char>(sc.ch);
+ sc.Forward();
+ }
+ // Special case: exponent with sign
+ if ((sc.chPrev == 'e' || sc.chPrev == 'E') &&
+ (sc.ch == '+' || sc.ch == '-')) {
+ number += static_cast<char>(sc.ch);
+ sc.Forward ();
+ while (!IsSeparatorOrDelimiterCharacter(sc.ch)) {
+ number += static_cast<char>(sc.ch);
+ sc.Forward();
+ }
+ }
+ sc.SetState(SCE_SPICE_DEFAULT);
+}
+
+static void ColouriseWhiteSpace(StyleContext& sc, bool& ) {
+ sc.SetState(SCE_SPICE_DEFAULT);
+ sc.ForwardSetState(SCE_SPICE_DEFAULT);
+}
+
+static void ColouriseWord(StyleContext& sc, WordList& keywords, WordList& keywords2, WordList& keywords3, bool& apostropheStartsAttribute) {
+ apostropheStartsAttribute = true;
+ sc.SetState(SCE_SPICE_IDENTIFIER);
+ SString word;
+ while (!sc.atLineEnd && !IsSeparatorOrDelimiterCharacter(sc.ch)) {
+ word += static_cast<char>(tolower(sc.ch));
+ sc.Forward();
+ }
+ if (keywords.InList(word.c_str())) {
+ sc.ChangeState(SCE_SPICE_KEYWORD);
+ if (word != "all") {
+ apostropheStartsAttribute = false;
+ }
+ }
+ else if (keywords2.InList(word.c_str())) {
+ sc.ChangeState(SCE_SPICE_KEYWORD2);
+ if (word != "all") {
+ apostropheStartsAttribute = false;
+ }
+ }
+ else if (keywords3.InList(word.c_str())) {
+ sc.ChangeState(SCE_SPICE_KEYWORD3);
+ if (word != "all") {
+ apostropheStartsAttribute = false;
+ }
+ }
+ sc.SetState(SCE_SPICE_DEFAULT);
+}
+
+//
+// ColouriseDocument
+//
+static void ColouriseDocument(
+ unsigned int startPos,
+ int length,
+ int initStyle,
+ WordList *keywordlists[],
+ Accessor &styler) {
+ WordList &keywords = *keywordlists[0];
+ WordList &keywords2 = *keywordlists[1];
+ WordList &keywords3 = *keywordlists[2];
+ StyleContext sc(startPos, length, initStyle, styler);
+ int lineCurrent = styler.GetLine(startPos);
+ bool apostropheStartsAttribute = (styler.GetLineState(lineCurrent) & 1) != 0;
+ while (sc.More()) {
+ if (sc.atLineEnd) {
+ // Go to the next line
+ sc.Forward();
+ lineCurrent++;
+ // Remember the line state for future incremental lexing
+ styler.SetLineState(lineCurrent, apostropheStartsAttribute);
+ // Don't continue any styles on the next line
+ sc.SetState(SCE_SPICE_DEFAULT);
+ }
+ // Comments
+ if ((sc.Match('*') && sc.atLineStart) || sc.Match('*','~')) {
+ ColouriseComment(sc, apostropheStartsAttribute);
+ // Whitespace
+ } else if (IsASpace(sc.ch)) {
+ ColouriseWhiteSpace(sc, apostropheStartsAttribute);
+ // Delimiters
+ } else if (IsDelimiterCharacter(sc.ch)) {
+ ColouriseDelimiter(sc, apostropheStartsAttribute);
+ // Numbers
+ } else if (IsADigit(sc.ch) || sc.ch == '#') {
+ ColouriseNumber(sc, apostropheStartsAttribute);
+ // Keywords or identifiers
+ } else {
+ ColouriseWord(sc, keywords, keywords2, keywords3, apostropheStartsAttribute);
+ }
+ }
+ sc.Complete();
+}
+
+static inline bool IsDelimiterCharacter(int ch) {
+ switch (ch) {
+ case '&':
+ case '\'':
+ case '(':
+ case ')':
+ case '*':
+ case '+':
+ case ',':
+ case '-':
+ case '.':
+ case '/':
+ case ':':
+ case ';':
+ case '<':
+ case '=':
+ case '>':
+ case '|':
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline bool IsNumberCharacter(int ch) {
+ return IsNumberStartCharacter(ch) ||
+ ch == '_' ||
+ ch == '.' ||
+ ch == '#' ||
+ (ch >= 'a' && ch <= 'f') ||
+ (ch >= 'A' && ch <= 'F');
+}
+
+static inline bool IsNumberStartCharacter(int ch) {
+ return IsADigit(ch);
+}
+
+static inline bool IsSeparatorOrDelimiterCharacter(int ch) {
+ return IsASpace(ch) || IsDelimiterCharacter(ch);
+}
+
+static inline bool IsWordCharacter(int ch) {
+ return IsWordStartCharacter(ch) || IsADigit(ch);
+}
+
+static inline bool IsWordStartCharacter(int ch) {
+ return (isascii(ch) && isalpha(ch)) || ch == '_';
+}
diff --git a/src/LexTADS3.cpp b/src/LexTADS3.cpp
new file mode 100644
index 0000000..9938f63
--- /dev/null
+++ b/src/LexTADS3.cpp
@@ -0,0 +1,837 @@
+// Scintilla source code edit control
+/** @file LexTADS3.cxx
+ ** Lexer for TADS3.
+ **/
+/* Copyright 2005 by Michael Cartmell
+ * Parts copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
+ * In particular FoldTADS3Doc is derived from FoldCppDoc
+ * The License.txt file describes the conditions under which this software may
+ * be distributed.
+ */
+
+/*
+ * TADS3 is a language designed by Michael J. Roberts for the writing of text
+ * based games. TADS comes from Text Adventure Development System. It has good
+ * support for the processing and outputting of formatted text and much of a
+ * TADS program listing consists of strings.
+ *
+ * TADS has two types of strings, those enclosed in single quotes (') and those
+ * enclosed in double quotes ("). These strings have different symantics and
+ * can be given different highlighting if desired.
+ *
+ * There can be embedded within both types of strings html tags
+ * ( <tag key=value> ), library directives ( <.directive> ), and message
+ * parameters ( {The doctor's/his} ).
+ *
+ * Double quoted strings can also contain interpolated expressions
+ * ( << rug.moved ? ' and a hole in the floor. ' : nil >> ). These expressions
+ * may themselves contain single or double quoted strings, although the double
+ * quoted strings may not contain interpolated expressions.
+ *
+ * These embedded constructs influence the output and formatting and are an
+ * important part of a program and require highlighting.
+ *
+ * LINKS
+ * http://www.tads.org/
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+static const int T3_SINGLE_QUOTE = 1;
+static const int T3_INT_EXPRESSION = 2;
+
+static inline bool IsEOL(const int ch, const int chNext) {
+ return (ch == '\r' && chNext != '\n') || (ch == '\n');
+}
+
+static inline bool IsASpaceOrTab(const int ch) {
+ return ch == ' ' || ch == '\t';
+}
+
+static inline bool IsATADS3Operator(const int ch) {
+ return ch == '=' || ch == '{' || ch == '}' || ch == '(' || ch == ')'
+ || ch == '[' || ch == ']' || ch == ',' || ch == ':' || ch == ';'
+ || ch == '+' || ch == '-' || ch == '*' || ch == '/' || ch == '%'
+ || ch == '?' || ch == '!' || ch == '<' || ch == '>' || ch == '|'
+ || ch == '@' || ch == '&' || ch == '~';
+}
+
+static inline bool IsAWordChar(const int ch) {
+ return isalnum(ch) || ch == '_' || ch == '.';
+}
+
+static inline bool IsAWordStart(const int ch) {
+ return isalpha(ch) || ch == '_';
+}
+
+static inline bool IsAHexDigit(const int ch) {
+ int lch = tolower(ch);
+ return isdigit(lch) || lch == 'a' || lch == 'b' || lch == 'c'
+ || lch == 'd' || lch == 'e' || lch == 'f';
+}
+
+static inline bool IsAnHTMLChar(int ch) {
+ return isalnum(ch) || ch == '-' || ch == '_' || ch == '.';
+}
+
+static inline bool IsADirectiveChar(int ch) {
+ return isalnum(ch) || isspace(ch) || ch == '-' || ch == '/';
+}
+
+static inline bool IsANumberStart(StyleContext &sc) {
+ return isdigit(sc.ch)
+ || (!isdigit(sc.chPrev) && sc.ch == '.' && isdigit(sc.chNext));
+}
+
+inline static void ColouriseTADS3Operator(StyleContext &sc) {
+ int initState = sc.state;
+ sc.SetState(SCE_T3_OPERATOR);
+ sc.ForwardSetState(initState);
+}
+
+static void ColouriseTADSHTMLString(StyleContext &sc, int &lineState) {
+ int endState = sc.state;
+ int chQuote = sc.ch;
+ if (endState == SCE_T3_HTML_STRING) {
+ if (lineState&T3_SINGLE_QUOTE) {
+ endState = SCE_T3_S_STRING;
+ chQuote = '"';
+ } else if (lineState&T3_INT_EXPRESSION) {
+ endState = SCE_T3_X_STRING;
+ chQuote = '\'';
+ } else {
+ endState = SCE_T3_D_STRING;
+ chQuote = '\'';
+ }
+ } else {
+ sc.SetState(SCE_T3_HTML_STRING);
+ sc.Forward();
+ }
+ int chString = chQuote == '"'? '\'': '"';
+
+ while (sc.More()) {
+ if (IsEOL(sc.ch, sc.chNext)) {
+ return;
+ }
+ if (sc.ch == chQuote) {
+ sc.ForwardSetState(endState);
+ return;
+ }
+ if (sc.ch == chString) {
+ sc.SetState(endState);
+ return;
+ }
+ if (sc.Match('\\', static_cast<char>(chQuote))
+ || sc.Match('\\', static_cast<char>(chString))) {
+ sc.Forward(2);
+ } else {
+ sc.Forward();
+ }
+ }
+}
+
+static void ColouriseTADS3HTMLTagStart(StyleContext &sc) {
+ sc.SetState(SCE_T3_HTML_TAG);
+ sc.Forward();
+ if (sc.ch == '/') {
+ sc.Forward();
+ }
+ while (IsAnHTMLChar(sc.ch)) {
+ sc.Forward();
+ }
+}
+
+static void ColouriseTADS3HTMLTag(StyleContext &sc, int &lineState) {
+ int endState = sc.state;
+ int chQuote = '"';
+ int chString = '\'';
+ switch (endState) {
+ case SCE_T3_S_STRING:
+ ColouriseTADS3HTMLTagStart(sc);
+ sc.SetState(SCE_T3_HTML_DEFAULT);
+ chQuote = '\'';
+ chString = '"';
+ break;
+ case SCE_T3_D_STRING:
+ case SCE_T3_X_STRING:
+ ColouriseTADS3HTMLTagStart(sc);
+ sc.SetState(SCE_T3_HTML_DEFAULT);
+ break;
+ case SCE_T3_HTML_DEFAULT:
+ if (lineState&T3_SINGLE_QUOTE) {
+ endState = SCE_T3_S_STRING;
+ chQuote = '\'';
+ chString = '"';
+ } else if (lineState&T3_INT_EXPRESSION) {
+ endState = SCE_T3_X_STRING;
+ } else {
+ endState = SCE_T3_D_STRING;
+ }
+ break;
+ }
+
+ while (sc.More()) {
+ if (IsEOL(sc.ch, sc.chNext)) {
+ return;
+ }
+ if (sc.Match('/', '>')) {
+ sc.SetState(SCE_T3_HTML_TAG);
+ sc.Forward(2);
+ sc.SetState(endState);
+ return;
+ }
+ if (sc.ch == '>') {
+ sc.SetState(SCE_T3_HTML_TAG);
+ sc.ForwardSetState(endState);
+ return;
+ }
+ if (sc.ch == chQuote) {
+ sc.SetState(endState);
+ return;
+ }
+ if (sc.ch == chString) {
+ ColouriseTADSHTMLString(sc, lineState);
+ } else if (sc.ch == '=') {
+ ColouriseTADS3Operator(sc);
+ } else {
+ sc.Forward();
+ }
+ }
+}
+
+static void ColouriseTADS3Keyword(StyleContext &sc,
+ WordList *keywordlists[], unsigned int endPos) {
+ char s[250];
+ WordList &keywords = *keywordlists[0];
+ WordList &userwords1 = *keywordlists[1];
+ WordList &userwords2 = *keywordlists[2];
+ WordList &userwords3 = *keywordlists[3];
+ int initState = sc.state;
+ sc.SetState(SCE_T3_IDENTIFIER);
+ while (sc.More() && (IsAWordChar(sc.ch))) {
+ sc.Forward();
+ }
+ sc.GetCurrent(s, sizeof(s));
+ if ( strcmp(s, "is") == 0 || strcmp(s, "not") == 0) {
+ // have to find if "in" is next
+ int n = 1;
+ while (n + sc.currentPos < endPos && IsASpaceOrTab(sc.GetRelative(n)))
+ n++;
+ if (sc.GetRelative(n) == 'i' && sc.GetRelative(n+1) == 'n') {
+ sc.Forward(n+2);
+ sc.ChangeState(SCE_T3_KEYWORD);
+ }
+ } else if (keywords.InList(s)) {
+ sc.ChangeState(SCE_T3_KEYWORD);
+ } else if (userwords3.InList(s)) {
+ sc.ChangeState(SCE_T3_USER3);
+ } else if (userwords2.InList(s)) {
+ sc.ChangeState(SCE_T3_USER2);
+ } else if (userwords1.InList(s)) {
+ sc.ChangeState(SCE_T3_USER1);
+ }
+ sc.SetState(initState);
+}
+
+static void ColouriseTADS3MsgParam(StyleContext &sc, int &lineState) {
+ int endState = sc.state;
+ int chQuote = '"';
+ switch (endState) {
+ case SCE_T3_S_STRING:
+ sc.SetState(SCE_T3_MSG_PARAM);
+ sc.Forward();
+ chQuote = '\'';
+ break;
+ case SCE_T3_D_STRING:
+ case SCE_T3_X_STRING:
+ sc.SetState(SCE_T3_MSG_PARAM);
+ sc.Forward();
+ break;
+ case SCE_T3_MSG_PARAM:
+ if (lineState&T3_SINGLE_QUOTE) {
+ endState = SCE_T3_S_STRING;
+ chQuote = '\'';
+ } else if (lineState&T3_INT_EXPRESSION) {
+ endState = SCE_T3_X_STRING;
+ } else {
+ endState = SCE_T3_D_STRING;
+ }
+ break;
+ }
+ while (sc.More() && sc.ch != '}' && sc.ch != chQuote) {
+ if (IsEOL(sc.ch, sc.chNext)) {
+ return;
+ }
+ if (sc.ch == '\\') {
+ sc.Forward();
+ }
+ sc.Forward();
+ }
+ if (sc.ch == chQuote) {
+ sc.SetState(endState);
+ } else {
+ sc.ForwardSetState(endState);
+ }
+}
+
+static void ColouriseTADS3LibDirective(StyleContext &sc, int &lineState) {
+ int initState = sc.state;
+ int chQuote = '"';
+ switch (initState) {
+ case SCE_T3_S_STRING:
+ sc.SetState(SCE_T3_LIB_DIRECTIVE);
+ sc.Forward(2);
+ chQuote = '\'';
+ break;
+ case SCE_T3_D_STRING:
+ sc.SetState(SCE_T3_LIB_DIRECTIVE);
+ sc.Forward(2);
+ break;
+ case SCE_T3_LIB_DIRECTIVE:
+ if (lineState&T3_SINGLE_QUOTE) {
+ initState = SCE_T3_S_STRING;
+ chQuote = '\'';
+ } else {
+ initState = SCE_T3_D_STRING;
+ }
+ break;
+ }
+ while (sc.More() && IsADirectiveChar(sc.ch)) {
+ if (IsEOL(sc.ch, sc.chNext)) {
+ return;
+ }
+ sc.Forward();
+ };
+ if (sc.ch == '>' || !sc.More()) {
+ sc.ForwardSetState(initState);
+ } else if (sc.ch == chQuote) {
+ sc.SetState(initState);
+ } else {
+ sc.ChangeState(initState);
+ sc.Forward();
+ }
+}
+
+static void ColouriseTADS3String(StyleContext &sc, int &lineState) {
+ int chQuote = sc.ch;
+ int endState = sc.state;
+ switch (sc.state) {
+ case SCE_T3_DEFAULT:
+ case SCE_T3_X_DEFAULT:
+ if (chQuote == '"') {
+ if (sc.state == SCE_T3_DEFAULT) {
+ sc.SetState(SCE_T3_D_STRING);
+ } else {
+ sc.SetState(SCE_T3_X_STRING);
+ }
+ lineState &= ~T3_SINGLE_QUOTE;
+ } else {
+ sc.SetState(SCE_T3_S_STRING);
+ lineState |= T3_SINGLE_QUOTE;
+ }
+ sc.Forward();
+ break;
+ case SCE_T3_S_STRING:
+ chQuote = '\'';
+ endState = lineState&T3_INT_EXPRESSION ?
+ SCE_T3_X_DEFAULT : SCE_T3_DEFAULT;
+ break;
+ case SCE_T3_D_STRING:
+ chQuote = '"';
+ endState = SCE_T3_DEFAULT;
+ break;
+ case SCE_T3_X_STRING:
+ chQuote = '"';
+ endState = SCE_T3_X_DEFAULT;
+ break;
+ }
+ while (sc.More()) {
+ if (IsEOL(sc.ch, sc.chNext)) {
+ return;
+ }
+ if (sc.ch == chQuote) {
+ sc.ForwardSetState(endState);
+ return;
+ }
+ if (sc.state == SCE_T3_D_STRING && sc.Match('<', '<')) {
+ lineState |= T3_INT_EXPRESSION;
+ sc.SetState(SCE_T3_X_DEFAULT);
+ sc.Forward(2);
+ return;
+ }
+ if (sc.Match('\\', static_cast<char>(chQuote))) {
+ sc.Forward(2);
+ } else if (sc.ch == '{') {
+ ColouriseTADS3MsgParam(sc, lineState);
+ } else if (sc.Match('<', '.')) {
+ ColouriseTADS3LibDirective(sc, lineState);
+ } else if (sc.ch == '<') {
+ ColouriseTADS3HTMLTag(sc, lineState);
+ } else {
+ sc.Forward();
+ }
+ }
+}
+
+static void ColouriseTADS3Comment(StyleContext &sc, int endState) {
+ sc.SetState(SCE_T3_BLOCK_COMMENT);
+ while (sc.More()) {
+ if (IsEOL(sc.ch, sc.chNext)) {
+ return;
+ }
+ if (sc.Match('*', '/')) {
+ sc.Forward(2);
+ sc.SetState(endState);
+ return;
+ }
+ sc.Forward();
+ }
+}
+
+static void ColouriseToEndOfLine(StyleContext &sc, int initState, int endState) {
+ sc.SetState(initState);
+ while (sc.More()) {
+ if (sc.ch == '\\') {
+ sc.Forward();
+ if (IsEOL(sc.ch, sc.chNext)) {
+ return;
+ }
+ }
+ if (IsEOL(sc.ch, sc.chNext)) {
+ sc.SetState(endState);
+ return;
+ }
+ sc.Forward();
+ }
+}
+
+static void ColouriseTADS3Number(StyleContext &sc) {
+ int endState = sc.state;
+ bool inHexNumber = false;
+ bool seenE = false;
+ bool seenDot = sc.ch == '.';
+ sc.SetState(SCE_T3_NUMBER);
+ if (sc.More()) {
+ sc.Forward();
+ }
+ if (sc.chPrev == '0' && tolower(sc.ch) == 'x') {
+ inHexNumber = true;
+ sc.Forward();
+ }
+ while (sc.More()) {
+ if (inHexNumber) {
+ if (!IsAHexDigit(sc.ch)) {
+ break;
+ }
+ } else if (!isdigit(sc.ch)) {
+ if (!seenE && tolower(sc.ch) == 'e') {
+ seenE = true;
+ seenDot = true;
+ if (sc.chNext == '+' || sc.chNext == '-') {
+ sc.Forward();
+ }
+ } else if (!seenDot && sc.ch == '.') {
+ seenDot = true;
+ } else {
+ break;
+ }
+ }
+ sc.Forward();
+ }
+ sc.SetState(endState);
+}
+
+static void ColouriseTADS3Doc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler) {
+ int visibleChars = 0;
+ int bracketLevel = 0;
+ int lineState = 0;
+ unsigned int endPos = startPos + length;
+ int lineCurrent = styler.GetLine(startPos);
+ if (lineCurrent > 0) {
+ lineState = styler.GetLineState(lineCurrent-1);
+ }
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ while (sc.More()) {
+
+ if (IsEOL(sc.ch, sc.chNext)) {
+ styler.SetLineState(lineCurrent, lineState);
+ lineCurrent++;
+ visibleChars = 0;
+ sc.Forward();
+ if (sc.ch == '\n') {
+ sc.Forward();
+ }
+ }
+
+ switch(sc.state) {
+ case SCE_T3_PREPROCESSOR:
+ case SCE_T3_LINE_COMMENT:
+ ColouriseToEndOfLine(sc, sc.state, lineState&T3_INT_EXPRESSION ?
+ SCE_T3_X_DEFAULT : SCE_T3_DEFAULT);
+ break;
+ case SCE_T3_S_STRING:
+ case SCE_T3_D_STRING:
+ case SCE_T3_X_STRING:
+ ColouriseTADS3String(sc, lineState);
+ visibleChars++;
+ break;
+ case SCE_T3_MSG_PARAM:
+ ColouriseTADS3MsgParam(sc, lineState);
+ break;
+ case SCE_T3_LIB_DIRECTIVE:
+ ColouriseTADS3LibDirective(sc, lineState);
+ break;
+ case SCE_T3_HTML_DEFAULT:
+ ColouriseTADS3HTMLTag(sc, lineState);
+ break;
+ case SCE_T3_HTML_STRING:
+ ColouriseTADSHTMLString(sc, lineState);
+ break;
+ case SCE_T3_BLOCK_COMMENT:
+ ColouriseTADS3Comment(sc, lineState&T3_INT_EXPRESSION ?
+ SCE_T3_X_DEFAULT : SCE_T3_DEFAULT);
+ break;
+ case SCE_T3_DEFAULT:
+ case SCE_T3_X_DEFAULT:
+ if (IsASpaceOrTab(sc.ch)) {
+ sc.Forward();
+ } else if (sc.ch == '#' && visibleChars == 0) {
+ ColouriseToEndOfLine(sc, SCE_T3_PREPROCESSOR, sc.state);
+ } else if (sc.Match('/', '*')) {
+ ColouriseTADS3Comment(sc, sc.state);
+ visibleChars++;
+ } else if (sc.Match('/', '/')) {
+ ColouriseToEndOfLine(sc, SCE_T3_LINE_COMMENT, sc.state);
+ } else if (sc.ch == '"') {
+ bracketLevel = 0;
+ ColouriseTADS3String(sc, lineState);
+ visibleChars++;
+ } else if (sc.ch == '\'') {
+ ColouriseTADS3String(sc, lineState);
+ visibleChars++;
+ } else if (sc.state == SCE_T3_X_DEFAULT && bracketLevel == 0
+ && sc.Match('>', '>')) {
+ sc.Forward(2);
+ sc.SetState(SCE_T3_D_STRING);
+ lineState &= ~(T3_SINGLE_QUOTE|T3_INT_EXPRESSION);
+ } else if (IsATADS3Operator(sc.ch)) {
+ if (sc.state == SCE_T3_X_DEFAULT) {
+ if (sc.ch == '(') {
+ bracketLevel++;
+ } else if (sc.ch == ')') {
+ bracketLevel--;
+ }
+ }
+ ColouriseTADS3Operator(sc);
+ visibleChars++;
+ } else if (IsANumberStart(sc)) {
+ ColouriseTADS3Number(sc);
+ visibleChars++;
+ } else if (IsAWordStart(sc.ch)) {
+ ColouriseTADS3Keyword(sc, keywordlists, endPos);
+ visibleChars++;
+ } else if (sc.Match("...")) {
+ sc.SetState(SCE_T3_IDENTIFIER);
+ sc.Forward(3);
+ sc.SetState(SCE_T3_DEFAULT);
+ } else {
+ sc.Forward();
+ visibleChars++;
+ }
+ break;
+ default:
+ sc.SetState(SCE_T3_DEFAULT);
+ sc.Forward();
+ }
+ }
+ sc.Complete();
+}
+
+/*
+ TADS3 has two styles of top level block (TLB). Eg
+
+ // default style
+ silverKey : Key 'small silver key' 'small silver key'
+ "A small key glints in the sunlight. "
+ ;
+
+ and
+
+ silverKey : Key {
+ 'small silver key'
+ 'small silver key'
+ "A small key glints in the sunlight. "
+ }
+
+ Some constructs mandate one or the other, but usually the author has may choose
+ either.
+
+ T3_SEENSTART is used to indicate that a braceless TLB has been (potentially)
+ seen and is also used to match the closing ';' of the default style.
+
+ T3_EXPECTINGIDENTIFIER and T3_EXPECTINGPUNCTUATION are used to keep track of
+ what characters may be seen without incrementing the block level. The general
+ pattern is identifier <punc> identifier, acceptable punctuation characters
+ are ':', ',', '(' and ')'. No attempt is made to ensure that punctuation
+ characters are syntactically correct, eg parentheses match. A ')' always
+ signifies the start of a block. We just need to check if it is followed by a
+ '{', in which case we let the brace handling code handle the folding level.
+
+ expectingIdentifier == false && expectingIdentifier == false
+ Before the start of a TLB.
+
+ expectingIdentifier == true && expectingIdentifier == true
+ Currently in an identifier. Will accept identifier or punctuation.
+
+ expectingIdentifier == true && expectingIdentifier == false
+ Just seen a punctuation character & now waiting for an identifier to start.
+
+ expectingIdentifier == false && expectingIdentifier == truee
+ We were in an identifier and have seen space. Now waiting to see a punctuation
+ character
+
+ Space, comments & preprocessor directives are always acceptable and are
+ equivalent.
+*/
+
+static const int T3_SEENSTART = 1 << 12;
+static const int T3_EXPECTINGIDENTIFIER = 1 << 13;
+static const int T3_EXPECTINGPUNCTUATION = 1 << 14;
+
+static inline bool IsStringTransition(int s1, int s2) {
+ return s1 != s2
+ && (s1 == SCE_T3_S_STRING || s1 == SCE_T3_X_STRING
+ || s1 == SCE_T3_D_STRING && s2 != SCE_T3_X_DEFAULT)
+ && s2 != SCE_T3_LIB_DIRECTIVE
+ && s2 != SCE_T3_MSG_PARAM
+ && s2 != SCE_T3_HTML_TAG
+ && s2 != SCE_T3_HTML_STRING;
+}
+
+static inline bool IsATADS3Punctuation(const int ch) {
+ return ch == ':' || ch == ',' || ch == '(' || ch == ')';
+}
+
+static inline bool IsAnIdentifier(const int style) {
+ return style == SCE_T3_IDENTIFIER
+ || style == SCE_T3_USER1
+ || style == SCE_T3_USER2
+ || style == SCE_T3_USER3;
+}
+
+static inline bool IsSpaceEquivalent(const int ch, const int style) {
+ return isspace(ch)
+ || style == SCE_T3_BLOCK_COMMENT
+ || style == SCE_T3_LINE_COMMENT
+ || style == SCE_T3_PREPROCESSOR;
+}
+
+static char peekAhead(unsigned int startPos, unsigned int endPos,
+ Accessor &styler) {
+ for (unsigned int i = startPos; i < endPos; i++) {
+ int style = styler.StyleAt(i);
+ char ch = styler[i];
+ if (!IsSpaceEquivalent(ch, style)) {
+ if (IsAnIdentifier(style)) {
+ return 'a';
+ }
+ if (IsATADS3Punctuation(ch)) {
+ return ':';
+ }
+ if (ch == '{') {
+ return '{';
+ }
+ return '*';
+ }
+ }
+ return ' ';
+}
+
+static void FoldTADS3Doc(unsigned int startPos, int length, int initStyle,
+ WordList *[], Accessor &styler) {
+ unsigned int endPos = startPos + length;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelCurrent = SC_FOLDLEVELBASE;
+ if (lineCurrent > 0)
+ levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
+ int seenStart = levelCurrent & T3_SEENSTART;
+ int expectingIdentifier = levelCurrent & T3_EXPECTINGIDENTIFIER;
+ int expectingPunctuation = levelCurrent & T3_EXPECTINGPUNCTUATION;
+ levelCurrent &= SC_FOLDLEVELNUMBERMASK;
+ int levelMinCurrent = levelCurrent;
+ int levelNext = levelCurrent;
+ char chNext = styler[startPos];
+ int styleNext = styler.StyleAt(startPos);
+ int style = initStyle;
+ char ch = chNext;
+ int stylePrev = style;
+ bool redo = false;
+ for (unsigned int i = startPos; i < endPos; i++) {
+ if (redo) {
+ redo = false;
+ i--;
+ } else {
+ ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ stylePrev = style;
+ style = styleNext;
+ styleNext = styler.StyleAt(i + 1);
+ }
+ bool atEOL = IsEOL(ch, chNext);
+
+ if (levelNext == SC_FOLDLEVELBASE) {
+ if (IsSpaceEquivalent(ch, style)) {
+ if (expectingPunctuation) {
+ expectingIdentifier = 0;
+ }
+ if (style == SCE_T3_BLOCK_COMMENT) {
+ levelNext++;
+ }
+ } else if (ch == '{') {
+ levelNext++;
+ seenStart = 0;
+ } else if (ch == '\'' || ch == '"' || ch == '[') {
+ levelNext++;
+ if (seenStart) {
+ redo = true;
+ }
+ } else if (ch == ';') {
+ seenStart = 0;
+ expectingIdentifier = 0;
+ expectingPunctuation = 0;
+ } else if (expectingIdentifier && expectingPunctuation) {
+ if (IsATADS3Punctuation(ch)) {
+ if (ch == ')' && peekAhead(i+1, endPos, styler) != '{') {
+ levelNext++;
+ } else {
+ expectingPunctuation = 0;
+ }
+ } else if (!IsAnIdentifier(style)) {
+ levelNext++;
+ }
+ } else if (expectingIdentifier && !expectingPunctuation) {
+ if (!IsAnIdentifier(style)) {
+ levelNext++;
+ } else {
+ expectingPunctuation = T3_EXPECTINGPUNCTUATION;
+ }
+ } else if (!expectingIdentifier && expectingPunctuation) {
+ if (!IsATADS3Punctuation(ch)) {
+ levelNext++;
+ } else {
+ if (ch == ')' && peekAhead(i+1, endPos, styler) != '{') {
+ levelNext++;
+ } else {
+ expectingIdentifier = T3_EXPECTINGIDENTIFIER;
+ expectingPunctuation = 0;
+ }
+ }
+ } else if (!expectingIdentifier && !expectingPunctuation) {
+ if (IsAnIdentifier(style)) {
+ seenStart = T3_SEENSTART;
+ expectingIdentifier = T3_EXPECTINGIDENTIFIER;
+ expectingPunctuation = T3_EXPECTINGPUNCTUATION;
+ }
+ }
+
+ if (levelNext != SC_FOLDLEVELBASE && style != SCE_T3_BLOCK_COMMENT) {
+ expectingIdentifier = 0;
+ expectingPunctuation = 0;
+ }
+
+ } else if (levelNext == SC_FOLDLEVELBASE+1 && seenStart
+ && ch == ';' && style == SCE_T3_OPERATOR ) {
+ levelNext--;
+ seenStart = 0;
+ } else if (style == SCE_T3_BLOCK_COMMENT) {
+ if (stylePrev != SCE_T3_BLOCK_COMMENT) {
+ levelNext++;
+ } else if (styleNext != SCE_T3_BLOCK_COMMENT && !atEOL) {
+ // Comments don't end at end of line and the next character may be unstyled.
+ levelNext--;
+ }
+ } else if (ch == '\'' || ch == '"') {
+ if (IsStringTransition(style, stylePrev)) {
+ if (levelMinCurrent > levelNext) {
+ levelMinCurrent = levelNext;
+ }
+ levelNext++;
+ } else if (IsStringTransition(style, styleNext)) {
+ levelNext--;
+ }
+ } else if (style == SCE_T3_OPERATOR) {
+ if (ch == '{' || ch == '[') {
+ // Measure the minimum before a '{' to allow
+ // folding on "} else {"
+ if (levelMinCurrent > levelNext) {
+ levelMinCurrent = levelNext;
+ }
+ levelNext++;
+ } else if (ch == '}' || ch == ']') {
+ levelNext--;
+ }
+ }
+
+ if (atEOL) {
+ if (seenStart && levelNext == SC_FOLDLEVELBASE) {
+ switch (peekAhead(i+1, endPos, styler)) {
+ case ' ':
+ case '{':
+ break;
+ case '*':
+ levelNext++;
+ break;
+ case 'a':
+ if (expectingPunctuation) {
+ levelNext++;
+ }
+ break;
+ case ':':
+ if (expectingIdentifier) {
+ levelNext++;
+ }
+ break;
+ }
+ if (levelNext != SC_FOLDLEVELBASE) {
+ expectingIdentifier = 0;
+ expectingPunctuation = 0;
+ }
+ }
+ int lev = levelMinCurrent | (levelNext | expectingIdentifier
+ | expectingPunctuation | seenStart) << 16;
+ if (levelMinCurrent < levelNext)
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ if (lev != styler.LevelAt(lineCurrent)) {
+ styler.SetLevel(lineCurrent, lev);
+ }
+ lineCurrent++;
+ levelCurrent = levelNext;
+ levelMinCurrent = levelCurrent;
+ }
+ }
+}
+
+static const char * const tads3WordList[] = {
+ "TADS3 Keywords",
+ "User defined 1",
+ "User defined 2",
+ "User defined 3",
+ 0
+};
+
+LexerModule lmTADS3(SCLEX_TADS3, ColouriseTADS3Doc, "tads3", FoldTADS3Doc, tads3WordList);
diff --git a/src/LexTCL.cpp b/src/LexTCL.cpp
new file mode 100644
index 0000000..c782141
--- /dev/null
+++ b/src/LexTCL.cpp
@@ -0,0 +1,362 @@
+// Scintilla source code edit control
+/** @file LexTCL.cxx
+ ** Lexer for TCL language.
+ **/
+// Copyright 1998-2001 by Andre Arpin <arpin@kingston.net>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+// Extended to accept accented characters
+static inline bool IsAWordChar(int ch) {
+ return ch >= 0x80 ||
+ (isalnum(ch) || ch == '_' || ch ==':' || ch=='.'); // : name space separator
+}
+
+static inline bool IsAWordStart(int ch) {
+ return ch >= 0x80 || (ch ==':' || isalpha(ch) || ch == '_');
+}
+
+static inline bool IsANumberChar(int ch) {
+ // Not exactly following number definition (several dots are seen as OK, etc.)
+ // but probably enough in most cases.
+ return (ch < 0x80) &&
+ (IsADigit(ch, 0x10) || toupper(ch) == 'E' ||
+ ch == '.' || ch == '-' || ch == '+');
+}
+
+static void ColouriseTCLDoc(unsigned int startPos, int length, int , WordList *keywordlists[], Accessor &styler) {
+#define isComment(s) (s==SCE_TCL_COMMENT || s==SCE_TCL_COMMENTLINE || s==SCE_TCL_COMMENT_BOX || s==SCE_TCL_BLOCK_COMMENT)
+ bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
+ bool commentLevel = false;
+ bool subBrace = false; // substitution begin with a brace ${.....}
+ enum tLineState {LS_DEFAULT, LS_OPEN_COMMENT, LS_OPEN_DOUBLE_QUOTE, LS_COMMENT_BOX, LS_MASK_STATE = 0xf,
+ LS_COMMAND_EXPECTED = 16, LS_BRACE_ONLY = 32 } lineState = LS_DEFAULT;
+ bool prevSlash = false;
+ int currentLevel = 0;
+ bool expected = 0;
+ bool subParen = 0;
+
+ int currentLine = styler.GetLine(startPos);
+ if (currentLine > 0)
+ currentLine--;
+ length += startPos - styler.LineStart(currentLine);
+ // make sure lines overlap
+ startPos = styler.LineStart(currentLine);
+
+ 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];
+ WordList &keywords9 = *keywordlists[8];
+
+ if (currentLine > 0) {
+ int ls = styler.GetLineState(currentLine - 1);
+ lineState = tLineState(ls & LS_MASK_STATE);
+ expected = LS_COMMAND_EXPECTED == tLineState(ls & LS_COMMAND_EXPECTED);
+ subBrace = LS_BRACE_ONLY == tLineState(ls & LS_BRACE_ONLY);
+ currentLevel = styler.LevelAt(currentLine - 1) >> 17;
+ commentLevel = (styler.LevelAt(currentLine - 1) >> 16) & 1;
+ } else
+ styler.SetLevel(0, SC_FOLDLEVELBASE | SC_FOLDLEVELHEADERFLAG);
+ bool visibleChars = false;
+
+ int previousLevel = currentLevel;
+ StyleContext sc(startPos, length, SCE_TCL_DEFAULT, styler);
+ for (; ; sc.Forward()) {
+next:
+ if (sc.ch=='\r' && sc.chNext == '\n') // only ignore \r on PC process on the mac
+ continue;
+ bool atEnd = !sc.More(); // make sure we coloured the last word
+ if (lineState != LS_DEFAULT) {
+ sc.SetState(SCE_TCL_DEFAULT);
+ if (lineState == LS_OPEN_COMMENT)
+ sc.SetState(SCE_TCL_COMMENTLINE);
+ else if (lineState == LS_OPEN_DOUBLE_QUOTE)
+ sc.SetState(SCE_TCL_IN_QUOTE);
+ else if (lineState == LS_COMMENT_BOX && (sc.ch == '#' || (sc.ch == ' ' && sc.chNext=='#')))
+ sc.SetState(SCE_TCL_COMMENT_BOX);
+ lineState = LS_DEFAULT;
+ }
+ if (subBrace) { // ${ overrides every thing even \ except }
+ if (sc.ch == '}') {
+ subBrace = false;
+ sc.SetState(SCE_TCL_OPERATOR);
+ sc.ForwardSetState(SCE_TCL_DEFAULT);
+ goto next;
+ }
+ else
+ sc.SetState(SCE_TCL_SUB_BRACE);
+ if (!sc.atLineEnd)
+ continue;
+ } else if (sc.state == SCE_TCL_DEFAULT || sc.state ==SCE_TCL_OPERATOR) {
+ expected &= isspacechar(static_cast<unsigned char>(sc.ch)) || IsAWordStart(sc.ch) || sc.ch =='#';
+ } else if (sc.state == SCE_TCL_SUBSTITUTION) {
+ switch(sc.ch) {
+ case '(':
+ subParen=true;
+ sc.SetState(SCE_TCL_OPERATOR);
+ sc.ForwardSetState(SCE_TCL_SUBSTITUTION);
+ continue;
+ case ')':
+ sc.SetState(SCE_TCL_OPERATOR);
+ subParen=false;
+ continue;
+ case '$':
+ continue;
+ case ',':
+ sc.SetState(SCE_TCL_OPERATOR);
+ if (subParen)
+ sc.ForwardSetState(SCE_TCL_SUBSTITUTION);
+ continue;
+ default :
+ // maybe spaces should be allowed ???
+ if (!IsAWordChar(sc.ch)) { // probably the code is wrong
+ sc.SetState(SCE_TCL_DEFAULT);
+ subParen = 0;
+ }
+ break;
+ }
+ } else if (isComment(sc.state)) {
+ } else if (!IsAWordChar(sc.ch)) {
+ if ((sc.state == SCE_TCL_IDENTIFIER && expected) || sc.state == SCE_TCL_MODIFIER) {
+ char w[100];
+ char *s=w;
+ sc.GetCurrent(w, sizeof(w));
+ if (w[strlen(w)-1]=='\r')
+ w[strlen(w)-1]=0;
+ while(*s == ':') // ignore leading : like in ::set a 10
+ ++s;
+ bool quote = sc.state == SCE_TCL_IN_QUOTE;
+ if (commentLevel || expected) {
+ if (keywords.InList(s)) {
+ sc.ChangeState(quote ? SCE_TCL_WORD_IN_QUOTE : SCE_TCL_WORD);
+ } else if (keywords2.InList(s)) {
+ sc.ChangeState(quote ? SCE_TCL_WORD_IN_QUOTE : SCE_TCL_WORD2);
+ } else if (keywords3.InList(s)) {
+ sc.ChangeState(quote ? SCE_TCL_WORD_IN_QUOTE : SCE_TCL_WORD3);
+ } else if (keywords4.InList(s)) {
+ sc.ChangeState(quote ? SCE_TCL_WORD_IN_QUOTE : SCE_TCL_WORD4);
+ } else if (sc.GetRelative(-static_cast<int>(strlen(s))-1) == '{' &&
+ keywords5.InList(s) && sc.ch == '}') { // {keyword} exactly no spaces
+ sc.ChangeState(SCE_TCL_EXPAND);
+ }
+ if (keywords6.InList(s)) {
+ sc.ChangeState(SCE_TCL_WORD5);
+ } else if (keywords7.InList(s)) {
+ sc.ChangeState(SCE_TCL_WORD6);
+ } else if (keywords8.InList(s)) {
+ sc.ChangeState(SCE_TCL_WORD7);
+ } else if (keywords9.InList(s)) {
+ sc.ChangeState(SCE_TCL_WORD8);
+ }
+ }
+ expected = false;
+ sc.SetState(quote ? SCE_TCL_IN_QUOTE : SCE_TCL_DEFAULT);
+ } else if (sc.state == SCE_TCL_MODIFIER || sc.state == SCE_TCL_IDENTIFIER) {
+ sc.SetState(SCE_TCL_DEFAULT);
+ }
+ }
+ if (atEnd)
+ break;
+ if (sc.atLineEnd) {
+ lineState = LS_DEFAULT;
+ currentLine = styler.GetLine(sc.currentPos);
+ if (foldComment && sc.state!=SCE_TCL_COMMENT && isComment(sc.state)) {
+ if (currentLevel == 0) {
+ ++currentLevel;
+ commentLevel = true;
+ }
+ } else {
+ if (visibleChars && commentLevel) {
+ --currentLevel;
+ --previousLevel;
+ commentLevel = false;
+ }
+ }
+ int flag = 0;
+ if (!visibleChars)
+ flag = SC_FOLDLEVELWHITEFLAG;
+ if (currentLevel > previousLevel)
+ flag = SC_FOLDLEVELHEADERFLAG;
+ styler.SetLevel(currentLine, flag + previousLevel + SC_FOLDLEVELBASE + (currentLevel << 17) + (commentLevel << 16));
+
+ // Update the line state, so it can be seen by next line
+ if (sc.state == SCE_TCL_IN_QUOTE)
+ lineState = LS_OPEN_DOUBLE_QUOTE;
+ else {
+ if (prevSlash) {
+ if (isComment(sc.state))
+ lineState = LS_OPEN_COMMENT;
+ } else if (sc.state == SCE_TCL_COMMENT_BOX)
+ lineState = LS_COMMENT_BOX;
+ }
+ styler.SetLineState(currentLine,
+ (subBrace ? LS_BRACE_ONLY : 0) |
+ (expected ? LS_COMMAND_EXPECTED : 0) | lineState);
+ if (lineState == LS_COMMENT_BOX)
+ sc.ForwardSetState(SCE_TCL_COMMENT_BOX);
+ else if (lineState == LS_OPEN_DOUBLE_QUOTE)
+ sc.ForwardSetState(SCE_TCL_IN_QUOTE);
+ else
+ sc.ForwardSetState(SCE_TCL_DEFAULT);
+ prevSlash = false;
+ previousLevel = currentLevel;
+ goto next;
+ }
+
+ if (prevSlash) {
+ prevSlash = false;
+ if (sc.ch == '#' && IsANumberChar(sc.chNext))
+ sc.ForwardSetState(SCE_TCL_NUMBER);
+ continue;
+ }
+ prevSlash = sc.ch == '\\';
+ if (isComment(sc.state))
+ continue;
+ if (sc.atLineStart) {
+ visibleChars = false;
+ if (sc.state!=SCE_TCL_IN_QUOTE && !isComment(sc.state))
+ {
+ sc.SetState(SCE_TCL_DEFAULT);
+ expected = IsAWordStart(sc.ch)|| isspacechar(static_cast<unsigned char>(sc.ch));
+ }
+ }
+
+ switch (sc.state) {
+ case SCE_TCL_NUMBER:
+ if (!IsANumberChar(sc.ch))
+ sc.SetState(SCE_TCL_DEFAULT);
+ break;
+ case SCE_TCL_IN_QUOTE:
+ if (sc.ch == '"') {
+ sc.ForwardSetState(SCE_TCL_DEFAULT);
+ visibleChars = true; // necessary if a " is the first and only character on a line
+ goto next;
+ } else if (sc.ch == '[' || sc.ch == ']' || sc.ch == '$') {
+ sc.SetState(SCE_TCL_OPERATOR);
+ expected = sc.ch == '[';
+ sc.ForwardSetState(SCE_TCL_IN_QUOTE);
+ goto next;
+ }
+ continue;
+ case SCE_TCL_OPERATOR:
+ sc.SetState(SCE_TCL_DEFAULT);
+ break;
+ }
+
+ if (sc.ch == '#') {
+ if (visibleChars) {
+ if (sc.state != SCE_TCL_IN_QUOTE && expected)
+ sc.SetState(SCE_TCL_COMMENT);
+ } else {
+ sc.SetState(SCE_TCL_COMMENTLINE);
+ if (sc.chNext == '~')
+ sc.SetState(SCE_TCL_BLOCK_COMMENT);
+ if (sc.atLineStart && (sc.chNext == '#' || sc.chNext == '-'))
+ sc.SetState(SCE_TCL_COMMENT_BOX);
+ }
+ }
+
+ if (!isspacechar(static_cast<unsigned char>(sc.ch))) {
+ visibleChars = true;
+ }
+
+ if (sc.ch == '\\') {
+ prevSlash = true;
+ continue;
+ }
+
+ // Determine if a new state should be entered.
+ if (sc.state == SCE_TCL_DEFAULT) {
+ if (IsAWordStart(sc.ch)) {
+ sc.SetState(SCE_TCL_IDENTIFIER);
+ } else if (IsADigit(sc.ch) && !IsAWordChar(sc.chPrev)) {
+ sc.SetState(SCE_TCL_NUMBER);
+ } else {
+ switch (sc.ch) {
+ case '\"':
+ sc.SetState(SCE_TCL_IN_QUOTE);
+ break;
+ case '{':
+ sc.SetState(SCE_TCL_OPERATOR);
+ expected = true;
+ ++currentLevel;
+ break;
+ case '}':
+ sc.SetState(SCE_TCL_OPERATOR);
+ --currentLevel;
+ break;
+ case '[':
+ expected = true;
+ case ']':
+ case '(':
+ case ')':
+ sc.SetState(SCE_TCL_OPERATOR);
+ break;
+ case ';':
+ expected = true;
+ break;
+ case '$':
+ subParen = 0;
+ if (sc.chNext != '{') {
+ sc.SetState(SCE_TCL_SUBSTITUTION);
+ }
+ else {
+ sc.SetState(SCE_TCL_OPERATOR); // $
+ sc.Forward(); // {
+ sc.ForwardSetState(SCE_TCL_SUB_BRACE);
+ subBrace = true;
+ }
+ break;
+ case '#':
+ if ((isspacechar(static_cast<unsigned char>(sc.chPrev))||
+ isoperator(static_cast<char>(sc.chPrev))) && IsADigit(sc.chNext,0x10))
+ sc.SetState(SCE_TCL_NUMBER);
+ break;
+ case '-':
+ sc.SetState(IsADigit(sc.chNext)? SCE_TCL_NUMBER: SCE_TCL_MODIFIER);
+ break;
+ default:
+ if (isoperator(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_TCL_OPERATOR);
+ }
+ }
+ }
+ }
+ }
+ sc.Complete();
+}
+
+static const char * const tclWordListDesc[] = {
+ "TCL Keywords",
+ "TK Keywords",
+ "iTCL Keywords",
+ "tkCommands",
+ "expand"
+ "user1",
+ "user2",
+ "user3",
+ "user4",
+ 0
+ };
+
+// this code supports folding in the colourizer
+LexerModule lmTCL(SCLEX_TCL, ColouriseTCLDoc, "tcl", 0, tclWordListDesc);
diff --git a/src/LexTeX.cpp b/src/LexTeX.cpp
new file mode 100755
index 0000000..65e530a
--- /dev/null
+++ b/src/LexTeX.cpp
@@ -0,0 +1,288 @@
+// Scintilla source code edit control
+
+// File: LexTeX.cxx - general context conformant tex coloring scheme
+// Author: Hans Hagen - PRAGMA ADE - Hasselt NL - www.pragma-ade.com
+// Version: September 28, 2003
+
+// Copyright: 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+// This lexer is derived from the one written for the texwork environment (1999++) which in
+// turn is inspired on texedit (1991++) which finds its roots in wdt (1986).
+
+// If you run into strange boundary cases, just tell me and I'll look into it.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+#include "StyleContext.h"
+
+// val SCE_TEX_DEFAULT = 0
+// val SCE_TEX_SPECIAL = 1
+// val SCE_TEX_GROUP = 2
+// val SCE_TEX_SYMBOL = 3
+// val SCE_TEX_COMMAND = 4
+// val SCE_TEX_TEXT = 5
+
+// Definitions in SciTEGlobal.properties:
+//
+// TeX Highlighting
+//
+// # Default
+// style.tex.0=fore:#7F7F00
+// # Special
+// style.tex.1=fore:#007F7F
+// # Group
+// style.tex.2=fore:#880000
+// # Symbol
+// style.tex.3=fore:#7F7F00
+// # Command
+// style.tex.4=fore:#008800
+// # Text
+// style.tex.5=fore:#000000
+
+// lexer.tex.interface.default=0
+// lexer.tex.comment.process=0
+
+// todo: lexer.tex.auto.if
+
+// Auxiliary functions:
+
+static inline bool endOfLine(Accessor &styler, unsigned int i) {
+ return
+ (styler[i] == '\n') || ((styler[i] == '\r') && (styler.SafeGetCharAt(i + 1) != '\n')) ;
+}
+
+static inline bool isTeXzero(int ch) {
+ return
+ (ch == '%') ;
+}
+
+static inline bool isTeXone(int ch) {
+ return
+ (ch == '[') || (ch == ']') || (ch == '=') || (ch == '#') ||
+ (ch == '(') || (ch == ')') || (ch == '<') || (ch == '>') ||
+ (ch == '"') ;
+}
+
+static inline bool isTeXtwo(int ch) {
+ return
+ (ch == '{') || (ch == '}') || (ch == '$') ;
+}
+
+static inline bool isTeXthree(int ch) {
+ return
+ (ch == '~') || (ch == '^') || (ch == '_') || (ch == '&') ||
+ (ch == '-') || (ch == '+') || (ch == '\"') || (ch == '`') ||
+ (ch == '/') || (ch == '|') || (ch == '%') ;
+}
+
+static inline bool isTeXfour(int ch) {
+ return
+ (ch == '\\') ;
+}
+
+static inline bool isTeXfive(int ch) {
+ return
+ ((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) ||
+ (ch == '@') || (ch == '!') || (ch == '?') ;
+}
+
+static inline bool isTeXsix(int ch) {
+ return
+ (ch == ' ') ;
+}
+
+static inline bool isTeXseven(int ch) {
+ return
+ (ch == '^') ;
+}
+
+// Interface determination
+
+static int CheckTeXInterface(
+ unsigned int startPos,
+ int length,
+ Accessor &styler,
+ int defaultInterface) {
+
+ char lineBuffer[1024] ;
+ unsigned int linePos = 0 ;
+
+ // some day we can make something lexer.tex.mapping=(all,0)(nl,1)(en,2)...
+
+ if (styler.SafeGetCharAt(0) == '%') {
+ for (unsigned int i = 0; i < startPos + length; i++) {
+ lineBuffer[linePos++] = styler.SafeGetCharAt(i) ;
+ if (endOfLine(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) {
+ lineBuffer[linePos] = '\0';
+ if (strstr(lineBuffer, "interface=all")) {
+ return 0 ;
+ } else if (strstr(lineBuffer, "interface=tex")) {
+ return 1 ;
+ } else if (strstr(lineBuffer, "interface=nl")) {
+ return 2 ;
+ } else if (strstr(lineBuffer, "interface=en")) {
+ return 3 ;
+ } else if (strstr(lineBuffer, "interface=de")) {
+ return 4 ;
+ } else if (strstr(lineBuffer, "interface=cz")) {
+ return 5 ;
+ } else if (strstr(lineBuffer, "interface=it")) {
+ return 6 ;
+ } else if (strstr(lineBuffer, "interface=ro")) {
+ return 7 ;
+ } else if (strstr(lineBuffer, "interface=latex")) {
+ // we will move latex cum suis up to 91+ when more keyword lists are supported
+ return 8 ;
+ } else if (styler.SafeGetCharAt(1) == 'D' && strstr(lineBuffer, "%D \\module")) {
+ // better would be to limit the search to just one line
+ return 3 ;
+ } else {
+ return defaultInterface ;
+ }
+ }
+ }
+ }
+
+ return defaultInterface ;
+}
+
+static void ColouriseTeXDoc(
+ unsigned int startPos,
+ int length,
+ int,
+ WordList *keywordlists[],
+ Accessor &styler) {
+
+ styler.StartAt(startPos) ;
+ styler.StartSegment(startPos) ;
+
+ bool processComment = styler.GetPropertyInt("lexer.tex.comment.process", 0) == 1 ;
+ bool useKeywords = styler.GetPropertyInt("lexer.tex.use.keywords", 1) == 1 ;
+ bool autoIf = styler.GetPropertyInt("lexer.tex.auto.if", 1) == 1 ;
+ int defaultInterface = styler.GetPropertyInt("lexer.tex.interface.default", 1) ;
+
+ char key[100] ;
+ int k ;
+ bool newifDone = false ;
+ bool inComment = false ;
+
+ int currentInterface = CheckTeXInterface(startPos,length,styler,defaultInterface) ;
+
+ if (currentInterface == 0) {
+ useKeywords = false ;
+ currentInterface = 1 ;
+ }
+
+ WordList &keywords = *keywordlists[currentInterface-1] ;
+
+ StyleContext sc(startPos, length, SCE_TEX_TEXT, styler);
+
+ bool going = sc.More() ; // needed because of a fuzzy end of file state
+
+ for (; going; sc.Forward()) {
+
+ if (! sc.More()) { going = false ; } // we need to go one behind the end of text
+
+ if (inComment) {
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_TEX_TEXT) ;
+ newifDone = false ;
+ inComment = false ;
+ }
+ } else {
+ if (! isTeXfive(sc.ch)) {
+ if (sc.state == SCE_TEX_COMMAND) {
+ if (sc.LengthCurrent() == 1) { // \<noncstoken>
+ if (isTeXseven(sc.ch) && isTeXseven(sc.chNext)) {
+ sc.Forward(2) ; // \^^ and \^^<token>
+ }
+ sc.ForwardSetState(SCE_TEX_TEXT) ;
+ } else {
+ sc.GetCurrent(key, sizeof(key)-1) ;
+ k = strlen(key) ;
+ memmove(key,key+1,k) ; // shift left over escape token
+ key[k] = '\0' ;
+ k-- ;
+ if (! keywords || ! useKeywords) {
+ sc.SetState(SCE_TEX_COMMAND) ;
+ newifDone = false ;
+ } else if (k == 1) { //\<cstoken>
+ sc.SetState(SCE_TEX_COMMAND) ;
+ newifDone = false ;
+ } else if (keywords.InList(key)) {
+ sc.SetState(SCE_TEX_COMMAND) ;
+ newifDone = autoIf && (strcmp(key,"newif") == 0) ;
+ } else if (autoIf && ! newifDone && (key[0] == 'i') && (key[1] == 'f') && keywords.InList("if")) {
+ sc.SetState(SCE_TEX_COMMAND) ;
+ } else {
+ sc.ChangeState(SCE_TEX_TEXT) ;
+ sc.SetState(SCE_TEX_TEXT) ;
+ newifDone = false ;
+ }
+ }
+ }
+ if (isTeXzero(sc.ch)) {
+ sc.SetState(SCE_TEX_SYMBOL) ;
+ sc.ForwardSetState(SCE_TEX_DEFAULT) ;
+ inComment = ! processComment ;
+ newifDone = false ;
+ } else if (isTeXseven(sc.ch) && isTeXseven(sc.chNext)) {
+ sc.SetState(SCE_TEX_TEXT) ;
+ sc.ForwardSetState(SCE_TEX_TEXT) ;
+ } else if (isTeXone(sc.ch)) {
+ sc.SetState(SCE_TEX_SPECIAL) ;
+ newifDone = false ;
+ } else if (isTeXtwo(sc.ch)) {
+ sc.SetState(SCE_TEX_GROUP) ;
+ newifDone = false ;
+ } else if (isTeXthree(sc.ch)) {
+ sc.SetState(SCE_TEX_SYMBOL) ;
+ newifDone = false ;
+ } else if (isTeXfour(sc.ch)) {
+ sc.SetState(SCE_TEX_COMMAND) ;
+ } else if (isTeXsix(sc.ch)) {
+ sc.SetState(SCE_TEX_TEXT) ;
+ } else if (sc.atLineEnd) {
+ sc.SetState(SCE_TEX_TEXT) ;
+ newifDone = false ;
+ inComment = false ;
+ } else {
+ sc.SetState(SCE_TEX_TEXT) ;
+ }
+ } else if (sc.state != SCE_TEX_COMMAND) {
+ sc.SetState(SCE_TEX_TEXT) ;
+ }
+ }
+ }
+ sc.ChangeState(SCE_TEX_TEXT) ;
+ sc.Complete();
+
+}
+
+
+// Hooks into the system:
+
+static const char * const texWordListDesc[] = {
+ "TeX, eTeX, pdfTeX, Omega",
+ "ConTeXt Dutch",
+ "ConTeXt English",
+ "ConTeXt German",
+ "ConTeXt Czech",
+ "ConTeXt Italian",
+ "ConTeXt Romanian",
+ 0,
+} ;
+
+LexerModule lmTeX(SCLEX_TEX, ColouriseTeXDoc, "tex", 0, texWordListDesc);
diff --git a/src/LexVB.cpp b/src/LexVB.cpp
new file mode 100755
index 0000000..0a6a5e4
--- /dev/null
+++ b/src/LexVB.cpp
@@ -0,0 +1,278 @@
+// Scintilla source code edit control
+/** @file LexVB.cxx
+ ** Lexer for Visual Basic and VBScript.
+ **/
+// Copyright 1998-2005 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+// Internal state, highlighted as number
+#define SCE_B_FILENUMBER SCE_B_DEFAULT+100
+
+
+static bool IsVBComment(Accessor &styler, int pos, int len) {
+ return len > 0 && styler[pos] == '\'';
+}
+
+static inline bool IsTypeCharacter(int ch) {
+ return ch == '%' || ch == '&' || ch == '@' || ch == '!' || ch == '#' || ch == '$';
+}
+
+// Extended to accept accented characters
+static inline bool IsAWordChar(int ch) {
+ return ch >= 0x80 ||
+ (isalnum(ch) || ch == '.' || ch == '_');
+}
+
+static inline bool IsAWordStart(int ch) {
+ return ch >= 0x80 ||
+ (isalpha(ch) || ch == '_');
+}
+
+static inline bool IsANumberChar(int ch) {
+ // Not exactly following number definition (several dots are seen as OK, etc.)
+ // but probably enough in most cases.
+ return (ch < 0x80) &&
+ (isdigit(ch) || toupper(ch) == 'E' ||
+ ch == '.' || ch == '-' || ch == '+');
+}
+
+static void ColouriseVBDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler, bool vbScriptSyntax) {
+
+ WordList &keywords = *keywordlists[0];
+ WordList &keywords2 = *keywordlists[1];
+ WordList &keywords3 = *keywordlists[2];
+ WordList &keywords4 = *keywordlists[3];
+
+ styler.StartAt(startPos);
+
+ int visibleChars = 0;
+ int fileNbDigits = 0;
+
+ // Do not leak onto next line
+ if (initStyle == SCE_B_STRINGEOL || initStyle == SCE_B_COMMENT || initStyle == SCE_B_PREPROCESSOR) {
+ initStyle = SCE_B_DEFAULT;
+ }
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ for (; sc.More(); sc.Forward()) {
+
+ if (sc.state == SCE_B_OPERATOR) {
+ sc.SetState(SCE_B_DEFAULT);
+ } else if (sc.state == SCE_B_IDENTIFIER) {
+ if (!IsAWordChar(sc.ch)) {
+ // In Basic (except VBScript), a variable name or a function name
+ // can end with a special character indicating the type of the value
+ // held or returned.
+ bool skipType = false;
+ if (!vbScriptSyntax && IsTypeCharacter(sc.ch)) {
+ sc.Forward(); // Skip it
+ skipType = true;
+ }
+ if (sc.ch == ']') {
+ sc.Forward();
+ }
+ char s[100];
+ sc.GetCurrentLowered(s, sizeof(s));
+ if (skipType) {
+ s[strlen(s) - 1] = '\0';
+ }
+ if (strcmp(s, "rem") == 0) {
+ sc.ChangeState(SCE_B_COMMENT);
+ } else {
+ if (keywords.InList(s)) {
+ sc.ChangeState(SCE_B_KEYWORD);
+ } else if (keywords2.InList(s)) {
+ sc.ChangeState(SCE_B_KEYWORD2);
+ } else if (keywords3.InList(s)) {
+ sc.ChangeState(SCE_B_KEYWORD3);
+ } else if (keywords4.InList(s)) {
+ sc.ChangeState(SCE_B_KEYWORD4);
+ } // Else, it is really an identifier...
+ sc.SetState(SCE_B_DEFAULT);
+ }
+ }
+ } else if (sc.state == SCE_B_NUMBER) {
+ // We stop the number definition on non-numerical non-dot non-eE non-sign char
+ // Also accepts A-F for hex. numbers
+ if (!IsANumberChar(sc.ch) && !(tolower(sc.ch) >= 'a' && tolower(sc.ch) <= 'f')) {
+ sc.SetState(SCE_B_DEFAULT);
+ }
+ } else if (sc.state == SCE_B_STRING) {
+ // VB doubles quotes to preserve them, so just end this string
+ // state now as a following quote will start again
+ if (sc.ch == '\"') {
+ if (sc.chNext == '\"') {
+ sc.Forward();
+ } else {
+ if (tolower(sc.chNext) == 'c') {
+ sc.Forward();
+ }
+ sc.ForwardSetState(SCE_B_DEFAULT);
+ }
+ } else if (sc.atLineEnd) {
+ visibleChars = 0;
+ sc.ChangeState(SCE_B_STRINGEOL);
+ sc.ForwardSetState(SCE_B_DEFAULT);
+ }
+ } else if (sc.state == SCE_B_COMMENT) {
+ if (sc.atLineEnd) {
+ visibleChars = 0;
+ sc.ForwardSetState(SCE_B_DEFAULT);
+ }
+ } else if (sc.state == SCE_B_PREPROCESSOR) {
+ if (sc.atLineEnd) {
+ visibleChars = 0;
+ sc.ForwardSetState(SCE_B_DEFAULT);
+ }
+ } else if (sc.state == SCE_B_FILENUMBER) {
+ if (IsADigit(sc.ch)) {
+ fileNbDigits++;
+ if (fileNbDigits > 3) {
+ sc.ChangeState(SCE_B_DATE);
+ }
+ } else if (sc.ch == '\r' || sc.ch == '\n' || sc.ch == ',') {
+ // Regular uses: Close #1; Put #1, ...; Get #1, ... etc.
+ // Too bad if date is format #27, Oct, 2003# or something like that...
+ // Use regular number state
+ sc.ChangeState(SCE_B_NUMBER);
+ sc.SetState(SCE_B_DEFAULT);
+ } else if (sc.ch == '#') {
+ sc.ChangeState(SCE_B_DATE);
+ sc.ForwardSetState(SCE_B_DEFAULT);
+ } else {
+ sc.ChangeState(SCE_B_DATE);
+ }
+ if (sc.state != SCE_B_FILENUMBER) {
+ fileNbDigits = 0;
+ }
+ } else if (sc.state == SCE_B_DATE) {
+ if (sc.atLineEnd) {
+ visibleChars = 0;
+ sc.ChangeState(SCE_B_STRINGEOL);
+ sc.ForwardSetState(SCE_B_DEFAULT);
+ } else if (sc.ch == '#') {
+ sc.ForwardSetState(SCE_B_DEFAULT);
+ }
+ }
+
+ if (sc.state == SCE_B_DEFAULT) {
+ if (sc.ch == '\'') {
+ sc.SetState(SCE_B_COMMENT);
+ } else if (sc.ch == '\"') {
+ sc.SetState(SCE_B_STRING);
+ } else if (sc.ch == '#' && visibleChars == 0) {
+ // Preprocessor commands are alone on their line
+ sc.SetState(SCE_B_PREPROCESSOR);
+ } else if (sc.ch == '#') {
+ // It can be a date literal, ending with #, or a file number, from 1 to 511
+ // The date literal depends on the locale, so anything can go between #'s.
+ // Can be #January 1, 1993# or #1 Jan 93# or #05/11/2003#, etc.
+ // So we set the FILENUMBER state, and switch to DATE if it isn't a file number
+ sc.SetState(SCE_B_FILENUMBER);
+ } else if (sc.ch == '&' && tolower(sc.chNext) == 'h') {
+ // Hexadecimal number
+ sc.SetState(SCE_B_NUMBER);
+ sc.Forward();
+ } else if (sc.ch == '&' && tolower(sc.chNext) == 'o') {
+ // Octal number
+ sc.SetState(SCE_B_NUMBER);
+ sc.Forward();
+ } else if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
+ sc.SetState(SCE_B_NUMBER);
+ } else if (IsAWordStart(sc.ch) || (sc.ch == '[')) {
+ sc.SetState(SCE_B_IDENTIFIER);
+ } else if (isoperator(static_cast<char>(sc.ch)) || (sc.ch == '\\')) { // Integer division
+ sc.SetState(SCE_B_OPERATOR);
+ }
+ }
+
+ if (sc.atLineEnd) {
+ visibleChars = 0;
+ }
+ if (!IsASpace(sc.ch)) {
+ visibleChars++;
+ }
+ }
+ sc.Complete();
+}
+
+static void FoldVBDoc(unsigned int startPos, int length, int,
+ WordList *[], Accessor &styler) {
+ int endPos = startPos + length;
+
+ // Backtrack to previous line in case need to fix its fold status
+ int lineCurrent = styler.GetLine(startPos);
+ if (startPos > 0) {
+ if (lineCurrent > 0) {
+ lineCurrent--;
+ startPos = styler.LineStart(lineCurrent);
+ }
+ }
+ int spaceFlags = 0;
+ int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, IsVBComment);
+ char chNext = styler[startPos];
+ for (int i = startPos; i < endPos; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+
+ if ((ch == '\r' && chNext != '\n') || (ch == '\n') || (i == endPos)) {
+ int lev = indentCurrent;
+ int indentNext = styler.IndentAmount(lineCurrent + 1, &spaceFlags, IsVBComment);
+ if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG)) {
+ // Only non whitespace lines can be headers
+ if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK)) {
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ } else if (indentNext & SC_FOLDLEVELWHITEFLAG) {
+ // Line after is blank so check the next - maybe should continue further?
+ int spaceFlags2 = 0;
+ int indentNext2 = styler.IndentAmount(lineCurrent + 2, &spaceFlags2, IsVBComment);
+ if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext2 & SC_FOLDLEVELNUMBERMASK)) {
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ }
+ }
+ }
+ indentCurrent = indentNext;
+ styler.SetLevel(lineCurrent, lev);
+ lineCurrent++;
+ }
+ }
+}
+
+static void ColouriseVBNetDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler) {
+ ColouriseVBDoc(startPos, length, initStyle, keywordlists, styler, false);
+}
+
+static void ColouriseVBScriptDoc(unsigned int startPos, int length, int initStyle,
+ WordList *keywordlists[], Accessor &styler) {
+ ColouriseVBDoc(startPos, length, initStyle, keywordlists, styler, true);
+}
+
+static const char * const vbWordListDesc[] = {
+ "Keywords",
+ "user1",
+ "user2",
+ "user3",
+ 0
+};
+
+LexerModule lmVB(SCLEX_VB, ColouriseVBNetDoc, "vb", FoldVBDoc, vbWordListDesc);
+LexerModule lmVBScript(SCLEX_VBSCRIPT, ColouriseVBScriptDoc, "vbscript", FoldVBDoc, vbWordListDesc);
+
diff --git a/src/LexVHDL.cpp b/src/LexVHDL.cpp
new file mode 100755
index 0000000..0feef95
--- /dev/null
+++ b/src/LexVHDL.cpp
@@ -0,0 +1,473 @@
+// Scintilla source code edit control
+/** @file LexVHDL.cxx
+ ** Lexer for VHDL
+ ** Written by Phil Reid,
+ ** Based on:
+ ** - The Verilog Lexer by Avi Yegudin
+ ** - The Fortran Lexer by Chuan-jian Shen
+ ** - The C++ lexer by Neil Hodgson
+ **/
+// Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+static void ColouriseVHDLDoc(
+ unsigned int startPos,
+ int length,
+ int initStyle,
+ WordList *keywordlists[],
+ Accessor &styler);
+
+
+/***************************************/
+static inline bool IsAWordChar(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_' );
+}
+
+/***************************************/
+static inline bool IsAWordStart(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '_');
+}
+
+/***************************************/
+inline bool IsABlank(unsigned int ch) {
+ return (ch == ' ') || (ch == 0x09) || (ch == 0x0b) ;
+}
+
+/***************************************/
+static void ColouriseVHDLDoc(
+ unsigned int startPos,
+ int length,
+ int initStyle,
+ WordList *keywordlists[],
+ Accessor &styler)
+{
+ WordList &Keywords = *keywordlists[0];
+ WordList &Operators = *keywordlists[1];
+ WordList &Attributes = *keywordlists[2];
+ WordList &Functions = *keywordlists[3];
+ WordList &Packages = *keywordlists[4];
+ WordList &Types = *keywordlists[5];
+ WordList &User = *keywordlists[6];
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ for (; sc.More(); sc.Forward())
+ {
+
+ // Determine if the current state should terminate.
+ if (sc.state == SCE_VHDL_OPERATOR) {
+ sc.SetState(SCE_VHDL_DEFAULT);
+ } else if (sc.state == SCE_VHDL_NUMBER) {
+ if (!IsAWordChar(sc.ch) && (sc.ch != '#')) {
+ sc.SetState(SCE_VHDL_DEFAULT);
+ }
+ } else if (sc.state == SCE_VHDL_IDENTIFIER) {
+ if (!IsAWordChar(sc.ch) || (sc.ch == '.')) {
+ char s[100];
+ sc.GetCurrentLowered(s, sizeof(s));
+ if (Keywords.InList(s)) {
+ sc.ChangeState(SCE_VHDL_KEYWORD);
+ } else if (Operators.InList(s)) {
+ sc.ChangeState(SCE_VHDL_STDOPERATOR);
+ } else if (Attributes.InList(s)) {
+ sc.ChangeState(SCE_VHDL_ATTRIBUTE);
+ } else if (Functions.InList(s)) {
+ sc.ChangeState(SCE_VHDL_STDFUNCTION);
+ } else if (Packages.InList(s)) {
+ sc.ChangeState(SCE_VHDL_STDPACKAGE);
+ } else if (Types.InList(s)) {
+ sc.ChangeState(SCE_VHDL_STDTYPE);
+ } else if (User.InList(s)) {
+ sc.ChangeState(SCE_VHDL_USERWORD);
+ }
+ sc.SetState(SCE_VHDL_DEFAULT);
+ }
+ } else if (sc.state == SCE_VHDL_COMMENT || sc.state == SCE_V_COMMENTLINEBANG) {
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_VHDL_DEFAULT);
+ }
+ } else if (sc.state == SCE_VHDL_STRING) {
+ if (sc.ch == '\\') {
+ if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
+ sc.Forward();
+ }
+ } else if (sc.ch == '\"') {
+ sc.ForwardSetState(SCE_VHDL_DEFAULT);
+ } else if (sc.atLineEnd) {
+ sc.ChangeState(SCE_V_STRINGEOL);
+ sc.ForwardSetState(SCE_VHDL_DEFAULT);
+ }
+ }
+
+ // Determine if a new state should be entered.
+ if (sc.state == SCE_VHDL_DEFAULT) {
+ if (IsADigit(sc.ch) || (sc.ch == '.' && IsADigit(sc.chNext))) {
+ sc.SetState(SCE_VHDL_NUMBER);
+ } else if (IsAWordStart(sc.ch)) {
+ sc.SetState(SCE_VHDL_IDENTIFIER);
+ } else if (sc.Match('-', '-')) {
+ sc.SetState(SCE_VHDL_COMMENT);
+ sc.Forward();
+ } else if (sc.Match('-', '-')) {
+ if (sc.Match("--!")) // Nice to have a different comment style
+ sc.SetState(SCE_VHDL_COMMENTLINEBANG);
+ else
+ sc.SetState(SCE_VHDL_COMMENT);
+ } else if (sc.ch == '\"') {
+ sc.SetState(SCE_VHDL_STRING);
+ } else if (isoperator(static_cast<char>(sc.ch))) {
+ sc.SetState(SCE_VHDL_OPERATOR);
+ }
+ }
+ }
+ sc.Complete();
+}
+//=============================================================================
+static bool IsCommentLine(int line, Accessor &styler) {
+ int pos = styler.LineStart(line);
+ int eol_pos = styler.LineStart(line + 1) - 1;
+ for (int i = pos; i < eol_pos; i++) {
+ char ch = styler[i];
+ char chNext = styler[i+1];
+ if ((ch == '-') && (chNext == '-'))
+ return true;
+ else if (ch != ' ' && ch != '\t')
+ return false;
+ }
+ return false;
+}
+
+//=============================================================================
+// Folding the code
+static void FoldNoBoxVHDLDoc(
+ unsigned int startPos,
+ int length,
+ int initStyle,
+ Accessor &styler)
+{
+ // Decided it would be smarter to have the lexer have all keywords included. Therefore I
+ // don't check if the style for the keywords that I use to adjust the levels.
+ char words[] =
+ "architecture begin case component else elsif end entity generate loop package process record then "
+ "procedure function when";
+ WordList keywords;
+ keywords.Set(words);
+
+ bool foldComment = styler.GetPropertyInt("fold.comment", 1) != 0;
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ bool foldAtElse = styler.GetPropertyInt("fold.at.else", 1) != 0;
+ bool foldAtBegin = styler.GetPropertyInt("fold.at.Begin", 1) != 0;
+ bool foldAtParenthese = styler.GetPropertyInt("fold.at.Parenthese", 1) != 0;
+ //bool foldAtWhen = styler.GetPropertyInt("fold.at.When", 1) != 0; //< fold at when in case statements
+
+ int visibleChars = 0;
+ unsigned int endPos = startPos + length;
+
+ int lineCurrent = styler.GetLine(startPos);
+ int levelCurrent = SC_FOLDLEVELBASE;
+ if(lineCurrent > 0)
+ levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
+ //int levelMinCurrent = levelCurrent;
+ int levelMinCurrentElse = levelCurrent; //< Used for folding at 'else'
+ int levelMinCurrentBegin = levelCurrent; //< Used for folding at 'begin'
+ int levelNext = levelCurrent;
+
+ /***************************************/
+ int lastStart = 0;
+ char prevWord[32] = "";
+
+ /***************************************/
+ // Find prev word
+ // The logic for going up or down a level depends on a the previous keyword
+ // This code could be cleaned up.
+ int end = 0;
+ unsigned int j;
+ for(j = startPos; j>0; j--)
+ {
+ char ch = styler.SafeGetCharAt(j);
+ char chPrev = styler.SafeGetCharAt(j-1);
+ int style = styler.StyleAt(j);
+ int stylePrev = styler.StyleAt(j-1);
+ if ((stylePrev != SCE_VHDL_COMMENT) && (stylePrev != SCE_VHDL_STRING))
+ {
+ if(IsAWordChar(chPrev) && !IsAWordChar(ch))
+ {
+ end = j-1;
+ }
+ }
+ if ((style != SCE_VHDL_COMMENT) && (style != SCE_VHDL_STRING))
+ {
+ if(!IsAWordChar(chPrev) && IsAWordStart(ch) && (end != 0))
+ {
+ char s[32];
+ unsigned int k;
+ for(k=0; (k<31 ) && (k<end-j+1 ); k++) {
+ s[k] = static_cast<char>(tolower(styler[j+k]));
+ }
+ s[k] = '\0';
+
+ if(keywords.InList(s)) {
+ strcpy(prevWord, s);
+ break;
+ }
+ }
+ }
+ }
+ for(j=j+strlen(prevWord); j<endPos; j++)
+ {
+ char ch = styler.SafeGetCharAt(j);
+ int style = styler.StyleAt(j);
+ if ((style != SCE_VHDL_COMMENT) && (style != SCE_VHDL_STRING))
+ {
+ if((ch == ';') && (strcmp(prevWord, "end") == 0))
+ {
+ strcpy(prevWord, ";");
+ }
+ }
+ }
+
+ char chNext = styler[startPos];
+ char chPrev = '\0';
+ char chNextNonBlank;
+ int styleNext = styler.StyleAt(startPos);
+ int style = initStyle;
+ //Platform::DebugPrintf("Line[%04d] Prev[%20s] ************************* Level[%x]\n", lineCurrent+1, prevWord, levelCurrent);
+
+ /***************************************/
+ for (unsigned int i = startPos; i < endPos; i++)
+ {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ chPrev = styler.SafeGetCharAt(i - 1);
+ chNextNonBlank = chNext;
+ unsigned int j = i+1;
+ while(IsABlank(chNextNonBlank) && j<endPos)
+ {
+ j ++ ;
+ chNextNonBlank = styler.SafeGetCharAt(j);
+ }
+ style = styleNext;
+ styleNext = styler.StyleAt(i + 1);
+ bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
+
+ if (foldComment && atEOL && IsCommentLine(lineCurrent, styler))
+ {
+ if(!IsCommentLine(lineCurrent-1, styler) && IsCommentLine(lineCurrent+1, styler))
+ {
+ levelNext++;
+ }
+ else if(IsCommentLine(lineCurrent-1, styler) && !IsCommentLine(lineCurrent+1, styler))
+ {
+ levelNext--;
+ }
+ }
+
+ if ((style == SCE_VHDL_OPERATOR) && foldAtParenthese)
+ {
+ if(ch == '(') {
+ levelNext++;
+ } else if (ch == ')') {
+ levelNext--;
+ }
+ }
+
+ if ((style != SCE_VHDL_COMMENT) && (style != SCE_VHDL_STRING))
+ {
+ if((ch == ';') && (strcmp(prevWord, "end") == 0))
+ {
+ strcpy(prevWord, ";");
+ }
+
+ if(!IsAWordChar(chPrev) && IsAWordStart(ch))
+ {
+ lastStart = i;
+ }
+
+ if(iswordchar(ch) && !iswordchar(chNext)) {
+ char s[32];
+ unsigned int k;
+ for(k=0; (k<31 ) && (k<i-lastStart+1 ); k++) {
+ s[k] = static_cast<char>(tolower(styler[lastStart+k]));
+ }
+ s[k] = '\0';
+
+ if(keywords.InList(s))
+ {
+ if (
+ strcmp(s, "architecture") == 0 ||
+ strcmp(s, "case") == 0 ||
+ strcmp(s, "component") == 0 ||
+ strcmp(s, "entity") == 0 ||
+ strcmp(s, "generate") == 0 ||
+ strcmp(s, "loop") == 0 ||
+ strcmp(s, "package") ==0 ||
+ strcmp(s, "process") == 0 ||
+ strcmp(s, "record") == 0 ||
+ strcmp(s, "then") == 0)
+ {
+ if (strcmp(prevWord, "end") != 0)
+ {
+ if (levelMinCurrentElse > levelNext) {
+ levelMinCurrentElse = levelNext;
+ }
+ levelNext++;
+ }
+ } else if (
+ strcmp(s, "procedure") == 0 ||
+ strcmp(s, "function") == 0)
+ {
+ if (strcmp(prevWord, "end") != 0) // check for "end procedure" etc.
+ { // This code checks to see if the procedure / function is a definition within a "package"
+ // rather than the actual code in the body.
+ int BracketLevel = 0;
+ for(int j=i+1; j<styler.Length(); j++)
+ {
+ int LocalStyle = styler.StyleAt(j);
+ char LocalCh = styler.SafeGetCharAt(j);
+ if(LocalCh == '(') BracketLevel++;
+ if(LocalCh == ')') BracketLevel--;
+ if(
+ (BracketLevel == 0) &&
+ (LocalStyle != SCE_VHDL_COMMENT) &&
+ (LocalStyle != SCE_VHDL_STRING) &&
+ !iswordchar(styler.SafeGetCharAt(j-1)) &&
+ styler.Match(j, "is") &&
+ !iswordchar(styler.SafeGetCharAt(j+2)))
+ {
+ if (levelMinCurrentElse > levelNext) {
+ levelMinCurrentElse = levelNext;
+ }
+ levelNext++;
+ break;
+ }
+ if((BracketLevel == 0) && (LocalCh == ';'))
+ {
+ break;
+ }
+ }
+ }
+
+ } else if (strcmp(s, "end") == 0) {
+ levelNext--;
+ } else if(strcmp(s, "elsif") == 0) { // elsif is followed by then so folding occurs correctly
+ levelNext--;
+ } else if (strcmp(s, "else") == 0) {
+ if(strcmp(prevWord, "when") != 0) // ignore a <= x when y else z;
+ {
+ levelMinCurrentElse = levelNext - 1; // VHDL else is all on its own so just dec. the min level
+ }
+ } else if(
+ ((strcmp(s, "begin") == 0) && (strcmp(prevWord, "architecture") == 0)) ||
+ ((strcmp(s, "begin") == 0) && (strcmp(prevWord, "function") == 0)) ||
+ ((strcmp(s, "begin") == 0) && (strcmp(prevWord, "procedure") == 0)))
+ {
+ levelMinCurrentBegin = levelNext - 1;
+ }
+ //Platform::DebugPrintf("Line[%04d] Prev[%20s] Cur[%20s] Level[%x]\n", lineCurrent+1, prevWord, s, levelCurrent);
+ strcpy(prevWord, s);
+ }
+ }
+ }
+ if (atEOL) {
+ int levelUse = levelCurrent;
+
+ if (foldAtElse && (levelMinCurrentElse < levelUse)) {
+ levelUse = levelMinCurrentElse;
+ }
+ if (foldAtBegin && (levelMinCurrentBegin < levelUse)) {
+ levelUse = levelMinCurrentBegin;
+ }
+ int lev = levelUse | levelNext << 16;
+ if (visibleChars == 0 && foldCompact)
+ lev |= SC_FOLDLEVELWHITEFLAG;
+
+ if (levelUse < levelNext)
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ if (lev != styler.LevelAt(lineCurrent)) {
+ styler.SetLevel(lineCurrent, lev);
+ }
+ //Platform::DebugPrintf("Line[%04d] ---------------------------------------------------- Level[%x]\n", lineCurrent+1, levelCurrent);
+ lineCurrent++;
+ levelCurrent = levelNext;
+ //levelMinCurrent = levelCurrent;
+ levelMinCurrentElse = levelCurrent;
+ levelMinCurrentBegin = levelCurrent;
+ visibleChars = 0;
+ }
+ /***************************************/
+ if (!isspacechar(ch)) visibleChars++;
+ }
+
+ /***************************************/
+// Platform::DebugPrintf("Line[%04d] ---------------------------------------------------- Level[%x]\n", lineCurrent+1, levelCurrent);
+}
+
+//=============================================================================
+static void FoldVHDLDoc(unsigned int startPos, int length, int initStyle, WordList *[],
+ Accessor &styler) {
+ FoldNoBoxVHDLDoc(startPos, length, initStyle, styler);
+}
+
+//=============================================================================
+static const char * const VHDLWordLists[] = {
+ "Keywords",
+ "Operators",
+ "Attributes",
+ "Standard Functions",
+ "Standard Packages",
+ "Standard Types",
+ "User Words",
+ 0,
+ };
+
+
+LexerModule lmVHDL(SCLEX_VHDL, ColouriseVHDLDoc, "vhdl", FoldVHDLDoc, VHDLWordLists);
+
+
+// Keyword:
+// access after alias all architecture array assert attribute begin block body buffer bus case component
+// configuration constant disconnect downto else elsif end entity exit file for function generate generic
+// group guarded if impure in inertial inout is label library linkage literal loop map new next null of
+// on open others out package port postponed procedure process pure range record register reject report
+// return select severity shared signal subtype then to transport type unaffected units until use variable
+// wait when while with
+//
+// Operators:
+// abs and mod nand nor not or rem rol ror sla sll sra srl xnor xor
+//
+// Attributes:
+// left right low high ascending image value pos val succ pred leftof rightof base range reverse_range
+// length delayed stable quiet transaction event active last_event last_active last_value driving
+// driving_value simple_name path_name instance_name
+//
+// Std Functions:
+// now readline read writeline write endfile resolved to_bit to_bitvector to_stdulogic to_stdlogicvector
+// to_stdulogicvector to_x01 to_x01z to_UX01 rising_edge falling_edge is_x shift_left shift_right rotate_left
+// rotate_right resize to_integer to_unsigned to_signed std_match to_01
+//
+// Std Packages:
+// std ieee work standard textio std_logic_1164 std_logic_arith std_logic_misc std_logic_signed
+// std_logic_textio std_logic_unsigned numeric_bit numeric_std math_complex math_real vital_primitives
+// vital_timing
+//
+// Std Types:
+// boolean bit character severity_level integer real time delay_length natural positive string bit_vector
+// file_open_kind file_open_status line text side width std_ulogic std_ulogic_vector std_logic
+// std_logic_vector X01 X01Z UX01 UX01Z unsigned signed
+//
+
diff --git a/src/LexVerilog.cpp b/src/LexVerilog.cpp
new file mode 100755
index 0000000..43ef7eb
--- /dev/null
+++ b/src/LexVerilog.cpp
@@ -0,0 +1,299 @@
+// Scintilla source code edit control
+/** @file LexVerilog.cxx
+ ** Lexer for Verilog.
+ ** Written by Avi Yegudin, based on C++ lexer by Neil Hodgson
+ **/
+// Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+static inline bool IsAWordChar(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '.' || ch == '_' || ch == '\'');
+}
+
+static inline bool IsAWordStart(const int ch) {
+ return (ch < 0x80) && (isalnum(ch) || ch == '_' || ch == '$');
+}
+
+static void ColouriseVerilogDoc(unsigned int startPos, int length, int initStyle, WordList *keywordlists[],
+ Accessor &styler) {
+
+ WordList &keywords = *keywordlists[0];
+ WordList &keywords2 = *keywordlists[1];
+ WordList &keywords3 = *keywordlists[2];
+ WordList &keywords4 = *keywordlists[3];
+
+ // Do not leak onto next line
+ if (initStyle == SCE_V_STRINGEOL)
+ initStyle = SCE_V_DEFAULT;
+
+ StyleContext sc(startPos, length, initStyle, styler);
+
+ for (; sc.More(); sc.Forward()) {
+
+ if (sc.atLineStart && (sc.state == SCE_V_STRING)) {
+ // Prevent SCE_V_STRINGEOL from leaking back to previous line
+ sc.SetState(SCE_V_STRING);
+ }
+
+ // Handle line continuation generically.
+ if (sc.ch == '\\') {
+ if (sc.chNext == '\n' || sc.chNext == '\r') {
+ sc.Forward();
+ if (sc.ch == '\r' && sc.chNext == '\n') {
+ sc.Forward();
+ }
+ continue;
+ }
+ }
+
+ // Determine if the current state should terminate.
+ if (sc.state == SCE_V_OPERATOR) {
+ sc.SetState(SCE_V_DEFAULT);
+ } else if (sc.state == SCE_V_NUMBER) {
+ if (!IsAWordChar(sc.ch)) {
+ sc.SetState(SCE_V_DEFAULT);
+ }
+ } else if (sc.state == SCE_V_IDENTIFIER) {
+ if (!IsAWordChar(sc.ch) || (sc.ch == '.')) {
+ char s[100];
+ sc.GetCurrent(s, sizeof(s));
+ if (keywords.InList(s)) {
+ sc.ChangeState(SCE_V_WORD);
+ } else if (keywords2.InList(s)) {
+ sc.ChangeState(SCE_V_WORD2);
+ } else if (keywords3.InList(s)) {
+ sc.ChangeState(SCE_V_WORD3);
+ } else if (keywords4.InList(s)) {
+ sc.ChangeState(SCE_V_USER);
+ }
+ sc.SetState(SCE_V_DEFAULT);
+ }
+ } else if (sc.state == SCE_V_PREPROCESSOR) {
+ if (!IsAWordChar(sc.ch)) {
+ sc.SetState(SCE_V_DEFAULT);
+ }
+ } else if (sc.state == SCE_V_COMMENT) {
+ if (sc.Match('*', '/')) {
+ sc.Forward();
+ sc.ForwardSetState(SCE_V_DEFAULT);
+ }
+ } else if (sc.state == SCE_V_COMMENTLINE || sc.state == SCE_V_COMMENTLINEBANG) {
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_V_DEFAULT);
+ }
+ } else if (sc.state == SCE_V_STRING) {
+ if (sc.ch == '\\') {
+ if (sc.chNext == '\"' || sc.chNext == '\'' || sc.chNext == '\\') {
+ sc.Forward();
+ }
+ } else if (sc.ch == '\"') {
+ sc.ForwardSetState(SCE_V_DEFAULT);
+ } else if (sc.atLineEnd) {
+ sc.ChangeState(SCE_V_STRINGEOL);
+ sc.ForwardSetState(SCE_V_DEFAULT);
+ }
+ }
+
+ // Determine if a new state should be entered.
+ if (sc.state == SCE_V_DEFAULT) {
+ if (IsADigit(sc.ch) || (sc.ch == '\'') || (sc.ch == '.' && IsADigit(sc.chNext))) {
+ sc.SetState(SCE_V_NUMBER);
+ } else if (IsAWordStart(sc.ch)) {
+ sc.SetState(SCE_V_IDENTIFIER);
+ } else if (sc.Match('/', '*')) {
+ sc.SetState(SCE_V_COMMENT);
+ sc.Forward(); // Eat the * so it isn't used for the end of the comment
+ } else if (sc.Match('/', '/')) {
+ if (sc.Match("//!")) // Nice to have a different comment style
+ sc.SetState(SCE_V_COMMENTLINEBANG);
+ else
+ sc.SetState(SCE_V_COMMENTLINE);
+ } else if (sc.ch == '\"') {
+ sc.SetState(SCE_V_STRING);
+ } else if (sc.ch == '`') {
+ sc.SetState(SCE_V_PREPROCESSOR);
+ // Skip whitespace between ` and preprocessor word
+ do {
+ sc.Forward();
+ } while ((sc.ch == ' ' || sc.ch == '\t') && sc.More());
+ if (sc.atLineEnd) {
+ sc.SetState(SCE_V_DEFAULT);
+ }
+ } else if (isoperator(static_cast<char>(sc.ch)) || sc.ch == '@' || sc.ch == '#') {
+ sc.SetState(SCE_V_OPERATOR);
+ }
+ }
+ }
+ sc.Complete();
+}
+
+static bool IsStreamCommentStyle(int style) {
+ return style == SCE_V_COMMENT;
+}
+
+// Store both the current line's fold level and the next lines in the
+// level store to make it easy to pick up with each increment
+// and to make it possible to fiddle the current level for "} else {".
+static void FoldNoBoxVerilogDoc(unsigned int startPos, int length, int initStyle,
+ Accessor &styler) {
+ bool foldComment = styler.GetPropertyInt("fold.comment") != 0;
+ bool foldPreprocessor = styler.GetPropertyInt("fold.preprocessor") != 0;
+ bool foldCompact = styler.GetPropertyInt("fold.compact", 1) != 0;
+ bool foldAtElse = styler.GetPropertyInt("fold.at.else", 0) != 0;
+ // Verilog specific folding options:
+ // fold_at_module -
+ // Generally used methodology in verilog code is
+ // one module per file, so folding at module definition is useless.
+ // fold_at_brace/parenthese -
+ // Folding of long port lists can be convenient.
+ bool foldAtModule = styler.GetPropertyInt("fold.verilog.flags", 0) != 0;
+ bool foldAtBrace = 1;
+ bool foldAtParenthese = 1;
+
+ unsigned int endPos = startPos + length;
+ int visibleChars = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int levelCurrent = SC_FOLDLEVELBASE;
+ if (lineCurrent > 0)
+ levelCurrent = styler.LevelAt(lineCurrent-1) >> 16;
+ int levelMinCurrent = levelCurrent;
+ int levelNext = levelCurrent;
+ char chNext = styler[startPos];
+ int styleNext = styler.StyleAt(startPos);
+ int style = initStyle;
+ for (unsigned int i = startPos; i < endPos; i++) {
+ char ch = chNext;
+ chNext = styler.SafeGetCharAt(i + 1);
+ int stylePrev = style;
+ style = styleNext;
+ styleNext = styler.StyleAt(i + 1);
+ bool atEOL = (ch == '\r' && chNext != '\n') || (ch == '\n');
+ if (foldComment && IsStreamCommentStyle(style)) {
+ if (!IsStreamCommentStyle(stylePrev)) {
+ levelNext++;
+ } else if (!IsStreamCommentStyle(styleNext) && !atEOL) {
+ // Comments don't end at end of line and the next character may be unstyled.
+ levelNext--;
+ }
+ }
+ if (foldComment && (style == SCE_V_COMMENTLINE)) {
+ if ((ch == '/') && (chNext == '/')) {
+ char chNext2 = styler.SafeGetCharAt(i + 2);
+ if (chNext2 == '{') {
+ levelNext++;
+ } else if (chNext2 == '}') {
+ levelNext--;
+ }
+ }
+ }
+ if (foldPreprocessor && (style == SCE_V_PREPROCESSOR)) {
+ if (ch == '`') {
+ unsigned int j = i + 1;
+ while ((j < endPos) && IsASpaceOrTab(styler.SafeGetCharAt(j))) {
+ j++;
+ }
+ if (styler.Match(j, "if")) {
+ levelNext++;
+ } else if (styler.Match(j, "end")) {
+ levelNext--;
+ }
+ }
+ }
+ if (style == SCE_V_OPERATOR) {
+ if (foldAtParenthese) {
+ if (ch == '(') {
+ levelNext++;
+ } else if (ch == ')') {
+ levelNext--;
+ }
+ }
+ }
+ if (style == SCE_V_OPERATOR) {
+ if (foldAtBrace) {
+ if (ch == '{') {
+ levelNext++;
+ } else if (ch == '}') {
+ levelNext--;
+ }
+ }
+ }
+ if (style == SCE_V_WORD && stylePrev != SCE_V_WORD) {
+ unsigned int j = i;
+ if (styler.Match(j, "case") ||
+ styler.Match(j, "casex") ||
+ styler.Match(j, "casez") ||
+ styler.Match(j, "function") ||
+ styler.Match(j, "fork") ||
+ styler.Match(j, "table") ||
+ styler.Match(j, "task") ||
+ styler.Match(j, "specify") ||
+ styler.Match(j, "primitive") ||
+ styler.Match(j, "module") && foldAtModule ||
+ styler.Match(j, "begin")) {
+ levelNext++;
+ } else if (styler.Match(j, "endcase") ||
+ styler.Match(j, "endfunction") ||
+ styler.Match(j, "join") ||
+ styler.Match(j, "endtask") ||
+ styler.Match(j, "endtable") ||
+ styler.Match(j, "endspecify") ||
+ styler.Match(j, "endprimitive") ||
+ styler.Match(j, "endmodule") && foldAtModule ||
+ styler.Match(j, "end") && !IsAWordChar(styler.SafeGetCharAt(j+3))) {
+ levelNext--;
+ }
+ }
+ if (atEOL) {
+ int levelUse = levelCurrent;
+ if (foldAtElse) {
+ levelUse = levelMinCurrent;
+ }
+ int lev = levelUse | levelNext << 16;
+ if (visibleChars == 0 && foldCompact)
+ lev |= SC_FOLDLEVELWHITEFLAG;
+ if (levelUse < levelNext)
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ if (lev != styler.LevelAt(lineCurrent)) {
+ styler.SetLevel(lineCurrent, lev);
+ }
+ lineCurrent++;
+ levelCurrent = levelNext;
+ levelMinCurrent = levelCurrent;
+ visibleChars = 0;
+ }
+ if (!isspacechar(ch))
+ visibleChars++;
+ }
+}
+
+static void FoldVerilogDoc(unsigned int startPos, int length, int initStyle, WordList *[],
+ Accessor &styler) {
+ FoldNoBoxVerilogDoc(startPos, length, initStyle, styler);
+}
+
+static const char * const verilogWordLists[] = {
+ "Primary keywords and identifiers",
+ "Secondary keywords and identifiers",
+ "System Tasks",
+ "User defined tasks and identifiers",
+ "Unused",
+ 0,
+ };
+
+
+LexerModule lmVerilog(SCLEX_VERILOG, ColouriseVerilogDoc, "verilog", FoldVerilogDoc, verilogWordLists);
diff --git a/src/LexYAML.cpp b/src/LexYAML.cpp
new file mode 100755
index 0000000..e3053f8
--- /dev/null
+++ b/src/LexYAML.cpp
@@ -0,0 +1,305 @@
+// Scintilla source code edit control
+/** @file LexYAML.cxx
+ ** Lexer for YAML.
+ **/
+// Copyright 2003- by Sean O'Dell <sean@celsoft.com>
+// Release under the same license as Scintilla/SciTE.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+#include "KeyWords.h"
+#include "Scintilla.h"
+#include "SciLexer.h"
+
+static const char * const yamlWordListDesc[] = {
+ "Keywords",
+ 0
+};
+
+static inline bool AtEOL(Accessor &styler, unsigned int i) {
+ return (styler[i] == '\n') ||
+ ((styler[i] == '\r') && (styler.SafeGetCharAt(i + 1) != '\n'));
+}
+
+static unsigned int SpaceCount(char* lineBuffer) {
+ if (lineBuffer == NULL)
+ return 0;
+
+ char* headBuffer = lineBuffer;
+
+ while (*headBuffer == ' ')
+ headBuffer++;
+
+ return headBuffer - lineBuffer;
+}
+
+#define YAML_STATE_BITSIZE 16
+#define YAML_STATE_MASK (0xFFFF0000)
+#define YAML_STATE_DOCUMENT (1 << YAML_STATE_BITSIZE)
+#define YAML_STATE_VALUE (2 << YAML_STATE_BITSIZE)
+#define YAML_STATE_COMMENT (3 << YAML_STATE_BITSIZE)
+#define YAML_STATE_TEXT_PARENT (4 << YAML_STATE_BITSIZE)
+#define YAML_STATE_TEXT (5 << YAML_STATE_BITSIZE)
+
+static void ColouriseYAMLLine(
+ char *lineBuffer,
+ unsigned int currentLine,
+ unsigned int lengthLine,
+ unsigned int startLine,
+ unsigned int endPos,
+ WordList &keywords,
+ Accessor &styler) {
+
+ unsigned int i = 0;
+ bool bInQuotes = false;
+ unsigned int indentAmount = SpaceCount(lineBuffer);
+
+ if (currentLine > 0) {
+ int parentLineState = styler.GetLineState(currentLine - 1);
+
+ if ((parentLineState&YAML_STATE_MASK) == YAML_STATE_TEXT || (parentLineState&YAML_STATE_MASK) == YAML_STATE_TEXT_PARENT) {
+ unsigned int parentIndentAmount = parentLineState&(~YAML_STATE_MASK);
+ if (indentAmount > parentIndentAmount) {
+ styler.SetLineState(currentLine, YAML_STATE_TEXT | parentIndentAmount);
+ styler.ColourTo(endPos, SCE_YAML_TEXT);
+ return;
+ }
+ }
+ }
+ styler.SetLineState(currentLine, 0);
+ if (strncmp(lineBuffer, "---", 3) == 0) { // Document marker
+ styler.SetLineState(currentLine, YAML_STATE_DOCUMENT);
+ styler.ColourTo(endPos, SCE_YAML_DOCUMENT);
+ return;
+ }
+ // Skip initial spaces
+ while ((i < lengthLine) && lineBuffer[i] == ' ') { // YAML always uses space, never TABS or anything else
+ i++;
+ }
+ if (lineBuffer[i] == '\t') { // if we skipped all spaces, and we are NOT inside a text block, this is wrong
+ styler.ColourTo(endPos, SCE_YAML_ERROR);
+ return;
+ }
+ if (lineBuffer[i] == '#') { // Comment
+ styler.SetLineState(currentLine, YAML_STATE_COMMENT);
+ styler.ColourTo(endPos, SCE_YAML_COMMENT);
+ return;
+ }
+ while (i < lengthLine) {
+ if (lineBuffer[i] == '\'' || lineBuffer[i] == '\"') {
+ bInQuotes = !bInQuotes;
+ } else if (lineBuffer[i] == ':' && !bInQuotes) {
+ styler.ColourTo(startLine + i, SCE_YAML_IDENTIFIER);
+ // Non-folding scalar
+ i++;
+ while ((i < lengthLine) && isspacechar(lineBuffer[i]))
+ i++;
+ unsigned int endValue = lengthLine - 1;
+ while ((endValue >= i) && isspacechar(lineBuffer[endValue]))
+ endValue--;
+ lineBuffer[endValue + 1] = '\0';
+ if (lineBuffer[i] == '|' || lineBuffer[i] == '>') {
+ i++;
+ if (lineBuffer[i] == '+' || lineBuffer[i] == '-')
+ i++;
+ while ((i < lengthLine) && isspacechar(lineBuffer[i]))
+ i++;
+ if (lineBuffer[i] == '\0') {
+ styler.SetLineState(currentLine, YAML_STATE_TEXT_PARENT | indentAmount);
+ styler.ColourTo(endPos, SCE_YAML_DEFAULT);
+ return;
+ } else if (lineBuffer[i] == '#') {
+ styler.SetLineState(currentLine, YAML_STATE_TEXT_PARENT | indentAmount);
+ styler.ColourTo(startLine + i - 1, SCE_YAML_DEFAULT);
+ styler.ColourTo(endPos, SCE_YAML_COMMENT);
+ return;
+ } else {
+ styler.ColourTo(endPos, SCE_YAML_ERROR);
+ return;
+ }
+ }
+ styler.SetLineState(currentLine, YAML_STATE_VALUE);
+ if (lineBuffer[i] == '&' || lineBuffer[i] == '*') {
+ styler.ColourTo(endPos, SCE_YAML_REFERENCE);
+ return;
+ }
+ if (keywords.InList(&lineBuffer[i])) { // Convertible value (true/false, etc.)
+ styler.ColourTo(endPos, SCE_YAML_KEYWORD);
+ return;
+ } else {
+ unsigned int i2 = i;
+ while ((i < lengthLine) && lineBuffer[i]) {
+ if (!isdigit(lineBuffer[i]) && lineBuffer[i] != '-' && lineBuffer[i] != '.' && lineBuffer[i] != ',') {
+ styler.ColourTo(endPos, SCE_YAML_DEFAULT);
+ return;
+ }
+ i++;
+ }
+ if (i > i2) {
+ styler.ColourTo(endPos, SCE_YAML_NUMBER);
+ return;
+ }
+ }
+ break; // shouldn't get here, but just in case, the rest of the line is coloured the default
+ }
+ i++;
+ }
+ styler.ColourTo(endPos, SCE_YAML_DEFAULT);
+}
+
+static void ColouriseYAMLDoc(unsigned int startPos, int length, int, WordList *keywordLists[], Accessor &styler) {
+ char lineBuffer[1024];
+ styler.StartAt(startPos);
+ styler.StartSegment(startPos);
+ unsigned int linePos = 0;
+ unsigned int startLine = startPos;
+ unsigned int endPos = startPos + length;
+ unsigned int maxPos = styler.Length();
+ unsigned int lineCurrent = styler.GetLine(startPos);
+
+ for (unsigned int i = startPos; i < maxPos && i < endPos; i++) {
+ lineBuffer[linePos++] = styler[i];
+ if (AtEOL(styler, i) || (linePos >= sizeof(lineBuffer) - 1)) {
+ // End of line (or of line buffer) met, colourise it
+ lineBuffer[linePos] = '\0';
+ ColouriseYAMLLine(lineBuffer, lineCurrent, linePos, startLine, i, *keywordLists[0], styler);
+ linePos = 0;
+ startLine = i + 1;
+ lineCurrent++;
+ }
+ }
+ if (linePos > 0) { // Last line does not have ending characters
+ ColouriseYAMLLine(lineBuffer, lineCurrent, linePos, startLine, startPos + length - 1, *keywordLists[0], styler);
+ }
+}
+
+static bool IsCommentLine(int line, Accessor &styler) {
+ int pos = styler.LineStart(line);
+ if (styler[pos] == '#')
+ return true;
+ return false;
+}
+
+static void FoldYAMLDoc(unsigned int startPos, int length, int /*initStyle - unused*/,
+ WordList *[], Accessor &styler) {
+ const int maxPos = startPos + length;
+ const int maxLines = styler.GetLine(maxPos - 1); // Requested last line
+ const int docLines = styler.GetLine(styler.Length() - 1); // Available last line
+ const bool foldComment = styler.GetPropertyInt("fold.comment.yaml") != 0;
+
+ // Backtrack to previous non-blank line so we can determine indent level
+ // for any white space lines
+ // and so we can fix any preceding fold level (which is why we go back
+ // at least one line in all cases)
+ int spaceFlags = 0;
+ int lineCurrent = styler.GetLine(startPos);
+ int indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, NULL);
+ while (lineCurrent > 0) {
+ lineCurrent--;
+ indentCurrent = styler.IndentAmount(lineCurrent, &spaceFlags, NULL);
+ if (!(indentCurrent & SC_FOLDLEVELWHITEFLAG) &&
+ (!IsCommentLine(lineCurrent, styler)))
+ break;
+ }
+ int indentCurrentLevel = indentCurrent & SC_FOLDLEVELNUMBERMASK;
+
+ // Set up initial loop state
+ int prevComment = 0;
+ if (lineCurrent >= 1)
+ prevComment = foldComment && IsCommentLine(lineCurrent - 1, styler);
+
+ // Process all characters to end of requested range
+ // or comment that hangs over the end of the range. Cap processing in all cases
+ // to end of document (in case of unclosed comment at end).
+ while ((lineCurrent <= docLines) && ((lineCurrent <= maxLines) || prevComment)) {
+
+ // Gather info
+ int lev = indentCurrent;
+ int lineNext = lineCurrent + 1;
+ int indentNext = indentCurrent;
+ if (lineNext <= docLines) {
+ // Information about next line is only available if not at end of document
+ indentNext = styler.IndentAmount(lineNext, &spaceFlags, NULL);
+ }
+ const int comment = foldComment && IsCommentLine(lineCurrent, styler);
+ const int comment_start = (comment && !prevComment && (lineNext <= docLines) &&
+ IsCommentLine(lineNext, styler) && (lev > SC_FOLDLEVELBASE));
+ const int comment_continue = (comment && prevComment);
+ if (!comment)
+ indentCurrentLevel = indentCurrent & SC_FOLDLEVELNUMBERMASK;
+ if (indentNext & SC_FOLDLEVELWHITEFLAG)
+ indentNext = SC_FOLDLEVELWHITEFLAG | indentCurrentLevel;
+
+ if (comment_start) {
+ // Place fold point at start of a block of comments
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ } else if (comment_continue) {
+ // Add level to rest of lines in the block
+ lev = lev + 1;
+ }
+
+ // Skip past any blank lines for next indent level info; we skip also
+ // comments (all comments, not just those starting in column 0)
+ // which effectively folds them into surrounding code rather
+ // than screwing up folding.
+
+ while ((lineNext < docLines) &&
+ ((indentNext & SC_FOLDLEVELWHITEFLAG) ||
+ (lineNext <= docLines && IsCommentLine(lineNext, styler)))) {
+
+ lineNext++;
+ indentNext = styler.IndentAmount(lineNext, &spaceFlags, NULL);
+ }
+
+ const int levelAfterComments = indentNext & SC_FOLDLEVELNUMBERMASK;
+ const int levelBeforeComments = Platform::Maximum(indentCurrentLevel,levelAfterComments);
+
+ // Now set all the indent levels on the lines we skipped
+ // Do this from end to start. Once we encounter one line
+ // which is indented more than the line after the end of
+ // the comment-block, use the level of the block before
+
+ int skipLine = lineNext;
+ int skipLevel = levelAfterComments;
+
+ while (--skipLine > lineCurrent) {
+ int skipLineIndent = styler.IndentAmount(skipLine, &spaceFlags, NULL);
+
+ if ((skipLineIndent & SC_FOLDLEVELNUMBERMASK) > levelAfterComments)
+ skipLevel = levelBeforeComments;
+
+ int whiteFlag = skipLineIndent & SC_FOLDLEVELWHITEFLAG;
+
+ styler.SetLevel(skipLine, skipLevel | whiteFlag);
+ }
+
+ // Set fold header on non-comment line
+ if (!comment && !(indentCurrent & SC_FOLDLEVELWHITEFLAG) ) {
+ if ((indentCurrent & SC_FOLDLEVELNUMBERMASK) < (indentNext & SC_FOLDLEVELNUMBERMASK))
+ lev |= SC_FOLDLEVELHEADERFLAG;
+ }
+
+ // Keep track of block comment state of previous line
+ prevComment = comment_start || comment_continue;
+
+ // Set fold level for this line and move to next line
+ styler.SetLevel(lineCurrent, lev);
+ indentCurrent = indentNext;
+ lineCurrent = lineNext;
+ }
+
+ // NOTE: Cannot set level of last line here because indentCurrent doesn't have
+ // header flag set; the loop above is crafted to take care of this case!
+ //styler.SetLevel(lineCurrent, indentCurrent);
+}
+
+LexerModule lmYAML(SCLEX_YAML, ColouriseYAMLDoc, "yaml", FoldYAMLDoc, yamlWordListDesc);
diff --git a/src/License.txt b/src/License.txt
new file mode 100755
index 0000000..cbe25b2
--- /dev/null
+++ b/src/License.txt
@@ -0,0 +1,20 @@
+License for Scintilla and SciTE
+
+Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+
+All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation.
+
+NEIL HODGSON DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS, IN NO EVENT SHALL NEIL HODGSON BE LIABLE FOR ANY
+SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
+OR PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file
diff --git a/src/LineMarker.cpp b/src/LineMarker.cpp
new file mode 100755
index 0000000..6ded13c
--- /dev/null
+++ b/src/LineMarker.cpp
@@ -0,0 +1,301 @@
+// Scintilla source code edit control
+/** @file LineMarker.cxx
+ ** Defines the look of a line marker in the margin .
+ **/
+// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <string.h>
+
+#include "Platform.h"
+
+#include "Scintilla.h"
+#include "XPM.h"
+#include "LineMarker.h"
+
+void LineMarker::RefreshColourPalette(Palette &pal, bool want) {
+ pal.WantFind(fore, want);
+ pal.WantFind(back, want);
+ if (pxpm) {
+ pxpm->RefreshColourPalette(pal, want);
+ }
+}
+
+void LineMarker::SetXPM(const char *textForm) {
+ delete pxpm;
+ pxpm = new XPM(textForm);
+ markType = SC_MARK_PIXMAP;
+}
+
+void LineMarker::SetXPM(const char * const *linesForm) {
+ delete pxpm;
+ pxpm = new XPM(linesForm);
+ markType = SC_MARK_PIXMAP;
+}
+
+static void DrawBox(Surface *surface, int centreX, int centreY, int armSize, ColourAllocated fore, ColourAllocated back) {
+ PRectangle rc;
+ rc.left = centreX - armSize;
+ rc.top = centreY - armSize;
+ rc.right = centreX + armSize + 1;
+ rc.bottom = centreY + armSize + 1;
+ surface->RectangleDraw(rc, back, fore);
+}
+
+static void DrawCircle(Surface *surface, int centreX, int centreY, int armSize, ColourAllocated fore, ColourAllocated back) {
+ PRectangle rcCircle;
+ rcCircle.left = centreX - armSize;
+ rcCircle.top = centreY - armSize;
+ rcCircle.right = centreX + armSize + 1;
+ rcCircle.bottom = centreY + armSize + 1;
+ surface->Ellipse(rcCircle, back, fore);
+}
+
+static void DrawPlus(Surface *surface, int centreX, int centreY, int armSize, ColourAllocated fore) {
+ PRectangle rcV(centreX, centreY - armSize + 2, centreX + 1, centreY + armSize - 2 + 1);
+ surface->FillRectangle(rcV, fore);
+ PRectangle rcH(centreX - armSize + 2, centreY, centreX + armSize - 2 + 1, centreY+1);
+ surface->FillRectangle(rcH, fore);
+}
+
+static void DrawMinus(Surface *surface, int centreX, int centreY, int armSize, ColourAllocated fore) {
+ PRectangle rcH(centreX - armSize + 2, centreY, centreX + armSize - 2 + 1, centreY+1);
+ surface->FillRectangle(rcH, fore);
+}
+
+void LineMarker::Draw(Surface *surface, PRectangle &rcWhole, Font &fontForCharacter) {
+ if ((markType == SC_MARK_PIXMAP) && (pxpm)) {
+ pxpm->Draw(surface, rcWhole);
+ return;
+ }
+ // Restrict most shapes a bit
+ PRectangle rc = rcWhole;
+ rc.top++;
+ rc.bottom--;
+ int minDim = Platform::Minimum(rc.Width(), rc.Height());
+ minDim--; // Ensure does not go beyond edge
+ int centreX = (rc.right + rc.left) / 2;
+ int centreY = (rc.bottom + rc.top) / 2;
+ int dimOn2 = minDim / 2;
+ int dimOn4 = minDim / 4;
+ int blobSize = dimOn2-1;
+ int armSize = dimOn2-2;
+ if (rc.Width() > (rc.Height() * 2)) {
+ // Wide column is line number so move to left to try to avoid overlapping number
+ centreX = rc.left + dimOn2 + 1;
+ }
+ if (markType == SC_MARK_ROUNDRECT) {
+ PRectangle rcRounded = rc;
+ rcRounded.left = rc.left + 1;
+ rcRounded.right = rc.right - 1;
+ surface->RoundedRectangle(rcRounded, fore.allocated, back.allocated);
+ } else if (markType == SC_MARK_CIRCLE) {
+ PRectangle rcCircle;
+ rcCircle.left = centreX - dimOn2;
+ rcCircle.top = centreY - dimOn2;
+ rcCircle.right = centreX + dimOn2;
+ rcCircle.bottom = centreY + dimOn2;
+ surface->Ellipse(rcCircle, fore.allocated, back.allocated);
+ } else if (markType == SC_MARK_ARROW) {
+ Point pts[] = {
+ Point(centreX - dimOn4, centreY - dimOn2),
+ Point(centreX - dimOn4, centreY + dimOn2),
+ Point(centreX + dimOn2 - dimOn4, centreY),
+ };
+ surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
+ fore.allocated, back.allocated);
+
+ } else if (markType == SC_MARK_ARROWDOWN) {
+ Point pts[] = {
+ Point(centreX - dimOn2, centreY - dimOn4),
+ Point(centreX + dimOn2, centreY - dimOn4),
+ Point(centreX, centreY + dimOn2 - dimOn4),
+ };
+ surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
+ fore.allocated, back.allocated);
+
+ } else if (markType == SC_MARK_PLUS) {
+ Point pts[] = {
+ Point(centreX - armSize, centreY - 1),
+ Point(centreX - 1, centreY - 1),
+ Point(centreX - 1, centreY - armSize),
+ Point(centreX + 1, centreY - armSize),
+ Point(centreX + 1, centreY - 1),
+ Point(centreX + armSize, centreY -1),
+ Point(centreX + armSize, centreY +1),
+ Point(centreX + 1, centreY + 1),
+ Point(centreX + 1, centreY + armSize),
+ Point(centreX - 1, centreY + armSize),
+ Point(centreX - 1, centreY + 1),
+ Point(centreX - armSize, centreY + 1),
+ };
+ surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
+ fore.allocated, back.allocated);
+
+ } else if (markType == SC_MARK_MINUS) {
+ Point pts[] = {
+ Point(centreX - armSize, centreY - 1),
+ Point(centreX + armSize, centreY -1),
+ Point(centreX + armSize, centreY +1),
+ Point(centreX - armSize, centreY + 1),
+ };
+ surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
+ fore.allocated, back.allocated);
+
+ } else if (markType == SC_MARK_SMALLRECT) {
+ PRectangle rcSmall;
+ rcSmall.left = rc.left + 1;
+ rcSmall.top = rc.top + 2;
+ rcSmall.right = rc.right - 1;
+ rcSmall.bottom = rc.bottom - 2;
+ surface->RectangleDraw(rcSmall, fore.allocated, back.allocated);
+
+ } else if (markType == SC_MARK_EMPTY || markType == SC_MARK_BACKGROUND) {
+ // An invisible marker so don't draw anything
+
+ } else if (markType == SC_MARK_VLINE) {
+ surface->PenColour(back.allocated);
+ surface->MoveTo(centreX, rcWhole.top);
+ surface->LineTo(centreX, rcWhole.bottom);
+
+ } else if (markType == SC_MARK_LCORNER) {
+ surface->PenColour(back.allocated);
+ surface->MoveTo(centreX, rcWhole.top);
+ surface->LineTo(centreX, rc.top + dimOn2);
+ surface->LineTo(rc.right - 2, rc.top + dimOn2);
+
+ } else if (markType == SC_MARK_TCORNER) {
+ surface->PenColour(back.allocated);
+ surface->MoveTo(centreX, rcWhole.top);
+ surface->LineTo(centreX, rcWhole.bottom);
+ surface->MoveTo(centreX, rc.top + dimOn2);
+ surface->LineTo(rc.right - 2, rc.top + dimOn2);
+
+ } else if (markType == SC_MARK_LCORNERCURVE) {
+ surface->PenColour(back.allocated);
+ surface->MoveTo(centreX, rcWhole.top);
+ surface->LineTo(centreX, rc.top + dimOn2-3);
+ surface->LineTo(centreX+3, rc.top + dimOn2);
+ surface->LineTo(rc.right - 1, rc.top + dimOn2);
+
+ } else if (markType == SC_MARK_TCORNERCURVE) {
+ surface->PenColour(back.allocated);
+ surface->MoveTo(centreX, rcWhole.top);
+ surface->LineTo(centreX, rcWhole.bottom);
+
+ surface->MoveTo(centreX, rc.top + dimOn2-3);
+ surface->LineTo(centreX+3, rc.top + dimOn2);
+ surface->LineTo(rc.right - 1, rc.top + dimOn2);
+
+ } else if (markType == SC_MARK_BOXPLUS) {
+ surface->PenColour(back.allocated);
+ DrawBox(surface, centreX, centreY, blobSize, fore.allocated, back.allocated);
+ DrawPlus(surface, centreX, centreY, blobSize, back.allocated);
+
+ } else if (markType == SC_MARK_BOXPLUSCONNECTED) {
+ surface->PenColour(back.allocated);
+ DrawBox(surface, centreX, centreY, blobSize, fore.allocated, back.allocated);
+ DrawPlus(surface, centreX, centreY, blobSize, back.allocated);
+
+ surface->MoveTo(centreX, centreY + blobSize);
+ surface->LineTo(centreX, rcWhole.bottom);
+
+ surface->MoveTo(centreX, rcWhole.top);
+ surface->LineTo(centreX, centreY - blobSize);
+
+ } else if (markType == SC_MARK_BOXMINUS) {
+ surface->PenColour(back.allocated);
+ DrawBox(surface, centreX, centreY, blobSize, fore.allocated, back.allocated);
+ DrawMinus(surface, centreX, centreY, blobSize, back.allocated);
+
+ surface->MoveTo(centreX, centreY + blobSize);
+ surface->LineTo(centreX, rcWhole.bottom);
+
+ } else if (markType == SC_MARK_BOXMINUSCONNECTED) {
+ surface->PenColour(back.allocated);
+ DrawBox(surface, centreX, centreY, blobSize, fore.allocated, back.allocated);
+ DrawMinus(surface, centreX, centreY, blobSize, back.allocated);
+
+ surface->MoveTo(centreX, centreY + blobSize);
+ surface->LineTo(centreX, rcWhole.bottom);
+
+ surface->MoveTo(centreX, rcWhole.top);
+ surface->LineTo(centreX, centreY - blobSize);
+
+ } else if (markType == SC_MARK_CIRCLEPLUS) {
+ DrawCircle(surface, centreX, centreY, blobSize, fore.allocated, back.allocated);
+ surface->PenColour(back.allocated);
+ DrawPlus(surface, centreX, centreY, blobSize, back.allocated);
+
+ } else if (markType == SC_MARK_CIRCLEPLUSCONNECTED) {
+ DrawCircle(surface, centreX, centreY, blobSize, fore.allocated, back.allocated);
+ surface->PenColour(back.allocated);
+ DrawPlus(surface, centreX, centreY, blobSize, back.allocated);
+
+ surface->MoveTo(centreX, centreY + blobSize);
+ surface->LineTo(centreX, rcWhole.bottom);
+
+ surface->MoveTo(centreX, rcWhole.top);
+ surface->LineTo(centreX, centreY - blobSize);
+
+ } else if (markType == SC_MARK_CIRCLEMINUS) {
+ DrawCircle(surface, centreX, centreY, blobSize, fore.allocated, back.allocated);
+ surface->PenColour(back.allocated);
+ DrawMinus(surface, centreX, centreY, blobSize, back.allocated);
+
+ surface->MoveTo(centreX, centreY + blobSize);
+ surface->LineTo(centreX, rcWhole.bottom);
+
+ } else if (markType == SC_MARK_CIRCLEMINUSCONNECTED) {
+ DrawCircle(surface, centreX, centreY, blobSize, fore.allocated, back.allocated);
+ surface->PenColour(back.allocated);
+ DrawMinus(surface, centreX, centreY, blobSize, back.allocated);
+
+ surface->MoveTo(centreX, centreY + blobSize);
+ surface->LineTo(centreX, rcWhole.bottom);
+
+ surface->MoveTo(centreX, rcWhole.top);
+ surface->LineTo(centreX, centreY - blobSize);
+
+ } else if (markType >= SC_MARK_CHARACTER) {
+ char character[1];
+ character[0] = static_cast<char>(markType - SC_MARK_CHARACTER);
+ int width = surface->WidthText(fontForCharacter, character, 1);
+ rc.left += (rc.Width() - width) / 2;
+ rc.right = rc.left + width;
+ surface->DrawTextClipped(rc, fontForCharacter, rc.bottom - 2,
+ character, 1, fore.allocated, back.allocated);
+
+ } else if (markType == SC_MARK_DOTDOTDOT) {
+ int right = centreX - 6;
+ for (int b=0; b<3; b++) {
+ PRectangle rcBlob(right, rc.bottom - 4, right + 2, rc.bottom-2);
+ surface->FillRectangle(rcBlob, fore.allocated);
+ right += 5;
+ }
+ } else if (markType == SC_MARK_ARROWS) {
+ surface->PenColour(fore.allocated);
+ int right = centreX - 2;
+ for (int b=0; b<3; b++) {
+ surface->MoveTo(right - 4, centreY - 4);
+ surface->LineTo(right, centreY);
+ surface->LineTo(right - 5, centreY + 5);
+ right += 4;
+ }
+ } else if (markType == SC_MARK_SHORTARROW) {
+ Point pts[] = {
+ Point(centreX, centreY + dimOn2),
+ Point(centreX + dimOn2, centreY),
+ Point(centreX, centreY - dimOn2),
+ Point(centreX, centreY - dimOn4),
+ Point(centreX - dimOn4, centreY - dimOn4),
+ Point(centreX - dimOn4, centreY + dimOn4),
+ Point(centreX, centreY + dimOn4),
+ Point(centreX, centreY + dimOn2),
+ };
+ surface->Polygon(pts, sizeof(pts) / sizeof(pts[0]),
+ fore.allocated, back.allocated);
+ } else { // SC_MARK_FULLRECT
+ surface->FillRectangle(rcWhole, back.allocated);
+ }
+}
diff --git a/src/LineMarker.h b/src/LineMarker.h
new file mode 100755
index 0000000..8ebdce4
--- /dev/null
+++ b/src/LineMarker.h
@@ -0,0 +1,54 @@
+// Scintilla source code edit control
+/** @file LineMarker.h
+ ** Defines the look of a line marker in the margin .
+ **/
+// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef LINEMARKER_H
+#define LINEMARKER_H
+
+/**
+ */
+class LineMarker {
+public:
+ int markType;
+ ColourPair fore;
+ ColourPair back;
+ int alpha;
+ XPM *pxpm;
+ LineMarker() {
+ markType = SC_MARK_CIRCLE;
+ fore = ColourDesired(0,0,0);
+ back = ColourDesired(0xff,0xff,0xff);
+ alpha = SC_ALPHA_NOALPHA;
+ pxpm = NULL;
+ }
+ LineMarker(const LineMarker &) {
+ // Defined to avoid pxpm being blindly copied, not as real copy constructor
+ markType = SC_MARK_CIRCLE;
+ fore = ColourDesired(0,0,0);
+ back = ColourDesired(0xff,0xff,0xff);
+ alpha = SC_ALPHA_NOALPHA;
+ pxpm = NULL;
+ }
+ ~LineMarker() {
+ delete pxpm;
+ }
+ LineMarker &operator=(const LineMarker &) {
+ // Defined to avoid pxpm being blindly copied, not as real assignment operator
+ markType = SC_MARK_CIRCLE;
+ fore = ColourDesired(0,0,0);
+ back = ColourDesired(0xff,0xff,0xff);
+ alpha = SC_ALPHA_NOALPHA;
+ delete pxpm;
+ pxpm = NULL;
+ return *this;
+ }
+ void RefreshColourPalette(Palette &pal, bool want);
+ void SetXPM(const char *textForm);
+ void SetXPM(const char * const *linesForm);
+ void Draw(Surface *surface, PRectangle &rc, Font &fontForCharacter);
+};
+
+#endif
diff --git a/src/PropSet.cpp b/src/PropSet.cpp
new file mode 100755
index 0000000..c563d2b
--- /dev/null
+++ b/src/PropSet.cpp
@@ -0,0 +1,1170 @@
+// SciTE - Scintilla based Text Editor
+/** @file PropSet.cxx
+ ** A Java style properties file module.
+ **/
+// Copyright 1998-2003 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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+
+// The comparison and case changing functions here assume ASCII
+// or extended ASCII such as the normal Windows code page.
+
+static inline char MakeUpperCase(char ch) {
+ if (ch < 'a' || ch > 'z')
+ return ch;
+ else
+ return static_cast<char>(ch - 'a' + 'A');
+}
+
+static inline bool IsLetter(char ch) {
+ return ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'));
+}
+
+inline bool IsASpace(unsigned int ch) {
+ return (ch == ' ') || ((ch >= 0x09) && (ch <= 0x0d));
+}
+
+int CompareCaseInsensitive(const char *a, const char *b) {
+ while (*a && *b) {
+ if (*a != *b) {
+ char upperA = MakeUpperCase(*a);
+ char upperB = MakeUpperCase(*b);
+ if (upperA != upperB)
+ return upperA - upperB;
+ }
+ a++;
+ b++;
+ }
+ // Either *a or *b is nul
+ return *a - *b;
+}
+
+int CompareNCaseInsensitive(const char *a, const char *b, size_t len) {
+ while (*a && *b && len) {
+ if (*a != *b) {
+ char upperA = MakeUpperCase(*a);
+ 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;
+}
+
+bool EqualCaseInsensitive(const char *a, const char *b) {
+ return 0 == CompareCaseInsensitive(a, b);
+}
+
+// Since the CaseInsensitive functions declared in SString
+// are implemented here, I will for now put the non-inline
+// implementations of the SString members here as well, so
+// that I can quickly see what effect this has.
+
+SString::SString(int i) : sizeGrowth(sizeGrowthDefault) {
+ char number[32];
+ sprintf(number, "%0d", i);
+ s = StringAllocate(number);
+ sSize = sLen = (s) ? strlen(s) : 0;
+}
+
+SString::SString(double d, int precision) : sizeGrowth(sizeGrowthDefault) {
+ char number[32];
+ sprintf(number, "%.*f", precision, d);
+ s = StringAllocate(number);
+ sSize = sLen = (s) ? strlen(s) : 0;
+}
+
+bool SString::grow(lenpos_t lenNew) {
+ while (sizeGrowth * 6 < lenNew) {
+ sizeGrowth *= 2;
+ }
+ char *sNew = new char[lenNew + sizeGrowth + 1];
+ if (sNew) {
+ if (s) {
+ memcpy(sNew, s, sLen);
+ delete []s;
+ }
+ s = sNew;
+ s[sLen] = '\0';
+ sSize = lenNew + sizeGrowth;
+ }
+ return sNew != 0;
+}
+
+SString &SString::assign(const char *sOther, lenpos_t sSize_) {
+ if (!sOther) {
+ sSize_ = 0;
+ } else if (sSize_ == measure_length) {
+ sSize_ = strlen(sOther);
+ }
+ if (sSize > 0 && sSize_ <= sSize) { // Does not allocate new buffer if the current is big enough
+ if (s && sSize_) {
+ memcpy(s, sOther, sSize_);
+ }
+ s[sSize_] = '\0';
+ sLen = sSize_;
+ } else {
+ delete []s;
+ s = StringAllocate(sOther, sSize_);
+ if (s) {
+ sSize = sSize_; // Allow buffer bigger than real string, thus providing space to grow
+ sLen = sSize_;
+ } else {
+ sSize = sLen = 0;
+ }
+ }
+ return *this;
+}
+
+bool SString::operator==(const SString &sOther) const {
+ if ((s == 0) && (sOther.s == 0))
+ return true;
+ if ((s == 0) || (sOther.s == 0))
+ return false;
+ return strcmp(s, sOther.s) == 0;
+}
+
+bool SString::operator==(const char *sOther) const {
+ if ((s == 0) && (sOther == 0))
+ return true;
+ if ((s == 0) || (sOther == 0))
+ return false;
+ return strcmp(s, sOther) == 0;
+}
+
+SString SString::substr(lenpos_t subPos, lenpos_t subLen) const {
+ if (subPos >= sLen) {
+ return SString(); // return a null string if start index is out of bounds
+ }
+ if ((subLen == measure_length) || (subPos + subLen > sLen)) {
+ subLen = sLen - subPos; // can't substr past end of source string
+ }
+ return SString(s, subPos, subPos + subLen);
+}
+
+SString &SString::lowercase(lenpos_t subPos, lenpos_t subLen) {
+ if ((subLen == measure_length) || (subPos + subLen > sLen)) {
+ subLen = sLen - subPos; // don't apply past end of string
+ }
+ for (lenpos_t i = subPos; i < subPos + subLen; i++) {
+ if (s[i] < 'A' || s[i] > 'Z')
+ continue;
+ else
+ s[i] = static_cast<char>(s[i] - 'A' + 'a');
+ }
+ return *this;
+}
+
+SString &SString::uppercase(lenpos_t subPos, lenpos_t subLen) {
+ if ((subLen == measure_length) || (subPos + subLen > sLen)) {
+ subLen = sLen - subPos; // don't apply past end of string
+ }
+ for (lenpos_t i = subPos; i < subPos + subLen; i++) {
+ if (s[i] < 'a' || s[i] > 'z')
+ continue;
+ else
+ s[i] = static_cast<char>(s[i] - 'a' + 'A');
+ }
+ return *this;
+}
+
+SString &SString::append(const char *sOther, lenpos_t sLenOther, char sep) {
+ if (!sOther) {
+ return *this;
+ }
+ if (sLenOther == measure_length) {
+ sLenOther = strlen(sOther);
+ }
+ int lenSep = 0;
+ if (sLen && sep) { // Only add a separator if not empty
+ lenSep = 1;
+ }
+ lenpos_t lenNew = sLen + sLenOther + lenSep;
+ // Conservative about growing the buffer: don't do it, unless really needed
+ if ((lenNew < sSize) || (grow(lenNew))) {
+ if (lenSep) {
+ s[sLen] = sep;
+ sLen++;
+ }
+ memcpy(&s[sLen], sOther, sLenOther);
+ sLen += sLenOther;
+ s[sLen] = '\0';
+ }
+ return *this;
+}
+
+SString &SString::insert(lenpos_t pos, const char *sOther, lenpos_t sLenOther) {
+ if (!sOther || pos > sLen) {
+ return *this;
+ }
+ if (sLenOther == measure_length) {
+ sLenOther = strlen(sOther);
+ }
+ lenpos_t lenNew = sLen + sLenOther;
+ // Conservative about growing the buffer: don't do it, unless really needed
+ if ((lenNew < sSize) || grow(lenNew)) {
+ lenpos_t moveChars = sLen - pos + 1;
+ for (lenpos_t i = moveChars; i > 0; i--) {
+ s[pos + sLenOther + i - 1] = s[pos + i - 1];
+ }
+ memcpy(s + pos, sOther, sLenOther);
+ sLen = lenNew;
+ }
+ return *this;
+}
+
+/**
+ * Remove @a len characters from the @a pos position, included.
+ * Characters at pos + len and beyond replace characters at pos.
+ * If @a len is 0, or greater than the length of the string
+ * starting at @a pos, the string is just truncated at @a pos.
+ */
+void SString::remove(lenpos_t pos, lenpos_t len) {
+ if (pos >= sLen) {
+ return;
+ }
+ if (len < 1 || pos + len >= sLen) {
+ s[pos] = '\0';
+ sLen = pos;
+ } else {
+ for (lenpos_t i = pos; i < sLen - len + 1; i++) {
+ s[i] = s[i+len];
+ }
+ sLen -= len;
+ }
+}
+
+bool SString::startswith(const char *prefix) {
+ lenpos_t lenPrefix = strlen(prefix);
+ if (lenPrefix > sLen) {
+ return false;
+ }
+ return strncmp(s, prefix, lenPrefix) == 0;
+}
+
+bool SString::endswith(const char *suffix) {
+ lenpos_t lenSuffix = strlen(suffix);
+ if (lenSuffix > sLen) {
+ return false;
+ }
+ return strncmp(s + sLen - lenSuffix, suffix, lenSuffix) == 0;
+}
+
+int SString::search(const char *sFind, lenpos_t start) const {
+ if (start < sLen) {
+ const char *sFound = strstr(s + start, sFind);
+ if (sFound) {
+ return sFound - s;
+ }
+ }
+ return -1;
+}
+
+int SString::substitute(char chFind, char chReplace) {
+ int c = 0;
+ char *t = s;
+ while (t) {
+ t = strchr(t, chFind);
+ if (t) {
+ *t = chReplace;
+ t++;
+ c++;
+ }
+ }
+ return c;
+}
+
+int SString::substitute(const char *sFind, const char *sReplace) {
+ int c = 0;
+ lenpos_t lenFind = strlen(sFind);
+ lenpos_t lenReplace = strlen(sReplace);
+ int posFound = search(sFind);
+ while (posFound >= 0) {
+ remove(posFound, lenFind);
+ insert(posFound, sReplace, lenReplace);
+ posFound = search(sFind, posFound + lenReplace);
+ c++;
+ }
+ return c;
+}
+
+char *SContainer::StringAllocate(lenpos_t len) {
+ if (len != measure_length) {
+ return new char[len + 1];
+ } else {
+ return 0;
+ }
+}
+
+char *SContainer::StringAllocate(const char *s, lenpos_t len) {
+ if (s == 0) {
+ return 0;
+ }
+ if (len == measure_length) {
+ len = strlen(s);
+ }
+ char *sNew = new char[len + 1];
+ if (sNew) {
+ memcpy(sNew, s, len);
+ sNew[len] = '\0';
+ }
+ return sNew;
+}
+
+// End SString functions
+
+bool PropSet::caseSensitiveFilenames = false;
+
+PropSet::PropSet() {
+ superPS = 0;
+ for (int root = 0; root < hashRoots; root++)
+ props[root] = 0;
+}
+
+PropSet::~PropSet() {
+ superPS = 0;
+ Clear();
+}
+
+void PropSet::Set(const char *key, const char *val, int lenKey, int lenVal) {
+ if (!*key) // Empty keys are not supported
+ return;
+ if (lenKey == -1)
+ lenKey = static_cast<int>(strlen(key));
+ if (lenVal == -1)
+ lenVal = static_cast<int>(strlen(val));
+ unsigned int hash = HashString(key, lenKey);
+ for (Property *p = props[hash % hashRoots]; p; p = p->next) {
+ if ((hash == p->hash) &&
+ ((strlen(p->key) == static_cast<unsigned int>(lenKey)) &&
+ (0 == strncmp(p->key, key, lenKey)))) {
+ // Replace current value
+ delete [](p->val);
+ p->val = StringDup(val, lenVal);
+ return;
+ }
+ }
+ // Not found
+ Property *pNew = new Property;
+ if (pNew) {
+ pNew->hash = hash;
+ pNew->key = StringDup(key, lenKey);
+ pNew->val = StringDup(val, lenVal);
+ pNew->next = props[hash % hashRoots];
+ props[hash % hashRoots] = pNew;
+ }
+}
+
+void PropSet::Set(const char *keyVal) {
+ while (IsASpace(*keyVal))
+ keyVal++;
+ const char *endVal = keyVal;
+ while (*endVal && (*endVal != '\n'))
+ endVal++;
+ const char *eqAt = strchr(keyVal, '=');
+ if (eqAt) {
+ Set(keyVal, eqAt + 1, eqAt-keyVal, endVal - eqAt - 1);
+ } else if (*keyVal) { // No '=' so assume '=1'
+ Set(keyVal, "1", endVal-keyVal, 1);
+ }
+}
+
+void PropSet::Unset(const char *key, int lenKey) {
+ if (!*key) // Empty keys are not supported
+ return;
+ if (lenKey == -1)
+ lenKey = static_cast<int>(strlen(key));
+ unsigned int hash = HashString(key, lenKey);
+ Property *pPrev = NULL;
+ for (Property *p = props[hash % hashRoots]; p; p = p->next) {
+ if ((hash == p->hash) &&
+ ((strlen(p->key) == static_cast<unsigned int>(lenKey)) &&
+ (0 == strncmp(p->key, key, lenKey)))) {
+ if (pPrev)
+ pPrev->next = p->next;
+ else
+ props[hash % hashRoots] = p->next;
+ if (p == enumnext)
+ enumnext = p->next; // Not that anyone should mix enum and Set / Unset.
+ delete [](p->key);
+ delete [](p->val);
+ delete p;
+ return;
+ } else {
+ pPrev = p;
+ }
+ }
+}
+
+void PropSet::SetMultiple(const char *s) {
+ const char *eol = strchr(s, '\n');
+ while (eol) {
+ Set(s);
+ s = eol + 1;
+ eol = strchr(s, '\n');
+ }
+ Set(s);
+}
+
+SString PropSet::Get(const char *key) {
+ unsigned int hash = HashString(key, strlen(key));
+ for (Property *p = props[hash % hashRoots]; p; p = p->next) {
+ if ((hash == p->hash) && (0 == strcmp(p->key, key))) {
+ return p->val;
+ }
+ }
+ if (superPS) {
+ // Failed here, so try in base property set
+ return superPS->Get(key);
+ } else {
+ return "";
+ }
+}
+
+bool PropSet::IncludesVar(const char *value, const char *key) {
+ const char *var = strstr(value, "$(");
+ while (var) {
+ if (isprefix(var + 2, key) && (var[2 + strlen(key)] == ')')) {
+ // Found $(key) which would lead to an infinite loop so exit
+ return true;
+ }
+ var = strstr(var + 2, ")");
+ if (var)
+ var = strstr(var + 1, "$(");
+ }
+ return false;
+}
+
+
+// There is some inconsistency between GetExpanded("foo") and Expand("$(foo)").
+// A solution is to keep a stack of variables that have been expanded, so that
+// recursive expansions can be skipped. For now I'll just use the C++ stack
+// for that, through a recursive function and a simple chain of pointers.
+
+struct VarChain {
+ VarChain(const char*var_=NULL, const VarChain *link_=NULL): var(var_), link(link_) {}
+
+ bool contains(const char *testVar) const {
+ return (var && (0 == strcmp(var, testVar)))
+ || (link && link->contains(testVar));
+ }
+
+ const char *var;
+ const VarChain *link;
+};
+
+static int ExpandAllInPlace(PropSet &props, SString &withVars, int maxExpands, const VarChain &blankVars = VarChain()) {
+ int varStart = withVars.search("$(");
+ while ((varStart >= 0) && (maxExpands > 0)) {
+ int varEnd = withVars.search(")", varStart+2);
+ if (varEnd < 0) {
+ break;
+ }
+
+ // For consistency, when we see '$(ab$(cde))', expand the inner variable first,
+ // regardless whether there is actually a degenerate variable named 'ab$(cde'.
+ int innerVarStart = withVars.search("$(", varStart+2);
+ while ((innerVarStart > varStart) && (innerVarStart < varEnd)) {
+ varStart = innerVarStart;
+ innerVarStart = withVars.search("$(", varStart+2);
+ }
+
+ SString var(withVars.c_str(), varStart + 2, varEnd);
+ SString val = props.Get(var.c_str());
+
+ if (blankVars.contains(var.c_str())) {
+ val.clear(); // treat blankVar as an empty string (e.g. to block self-reference)
+ }
+
+ if (--maxExpands >= 0) {
+ maxExpands = ExpandAllInPlace(props, val, maxExpands, VarChain(var.c_str(), &blankVars));
+ }
+
+ withVars.remove(varStart, varEnd-varStart+1);
+ withVars.insert(varStart, val.c_str(), val.length());
+
+ varStart = withVars.search("$(");
+ }
+
+ return maxExpands;
+}
+
+
+SString PropSet::GetExpanded(const char *key) {
+ SString val = Get(key);
+ ExpandAllInPlace(*this, val, 100, VarChain(key));
+ return val;
+}
+
+SString PropSet::Expand(const char *withVars, int maxExpands) {
+ SString val = withVars;
+ ExpandAllInPlace(*this, val, maxExpands);
+ return val;
+}
+
+int PropSet::GetInt(const char *key, int defaultValue) {
+ SString val = GetExpanded(key);
+ if (val.length())
+ return val.value();
+ return defaultValue;
+}
+
+bool isprefix(const char *target, const char *prefix) {
+ while (*target && *prefix) {
+ if (*target != *prefix)
+ return false;
+ target++;
+ prefix++;
+ }
+ if (*prefix)
+ return false;
+ else
+ return true;
+}
+
+static bool IsSuffix(const char *target, const char *suffix, bool caseSensitive) {
+ size_t lentarget = strlen(target);
+ size_t lensuffix = strlen(suffix);
+ if (lensuffix > lentarget)
+ return false;
+ if (caseSensitive) {
+ for (int i = static_cast<int>(lensuffix) - 1; i >= 0; i--) {
+ if (target[i + lentarget - lensuffix] != suffix[i])
+ return false;
+ }
+ } else {
+ for (int i = static_cast<int>(lensuffix) - 1; i >= 0; i--) {
+ if (MakeUpperCase(target[i + lentarget - lensuffix]) !=
+ MakeUpperCase(suffix[i]))
+ return false;
+ }
+ }
+ return true;
+}
+
+SString PropSet::GetWild(const char *keybase, const char *filename) {
+ for (int root = 0; root < hashRoots; root++) {
+ for (Property *p = props[root]; p; p = p->next) {
+ if (isprefix(p->key, keybase)) {
+ char * orgkeyfile = p->key + strlen(keybase);
+ char *keyfile = NULL;
+
+ if (strstr(orgkeyfile, "$(") == orgkeyfile) {
+ char *cpendvar = strchr(orgkeyfile, ')');
+ if (cpendvar) {
+ *cpendvar = '\0';
+ SString s = GetExpanded(orgkeyfile + 2);
+ *cpendvar = ')';
+ keyfile = StringDup(s.c_str());
+ }
+ }
+ char *keyptr = keyfile;
+
+ if (keyfile == NULL)
+ keyfile = orgkeyfile;
+
+ for (;;) {
+ char *del = strchr(keyfile, ';');
+ if (del == NULL)
+ del = keyfile + strlen(keyfile);
+ char delchr = *del;
+ *del = '\0';
+ if (*keyfile == '*') {
+ if (IsSuffix(filename, keyfile + 1, caseSensitiveFilenames)) {
+ *del = delchr;
+ delete []keyptr;
+ return p->val;
+ }
+ } else if (0 == strcmp(keyfile, filename)) {
+ *del = delchr;
+ delete []keyptr;
+ return p->val;
+ }
+ if (delchr == '\0')
+ break;
+ *del = delchr;
+ keyfile = del + 1;
+ }
+ delete []keyptr;
+
+ if (0 == strcmp(p->key, keybase)) {
+ return p->val;
+ }
+ }
+ }
+ }
+ if (superPS) {
+ // Failed here, so try in base property set
+ return superPS->GetWild(keybase, filename);
+ } else {
+ return "";
+ }
+}
+
+
+
+// GetNewExpand does not use Expand as it has to use GetWild with the filename for each
+// variable reference found.
+SString PropSet::GetNewExpand(const char *keybase, const char *filename) {
+ char *base = StringDup(GetWild(keybase, filename).c_str());
+ char *cpvar = strstr(base, "$(");
+ int maxExpands = 1000; // Avoid infinite expansion of recursive definitions
+ while (cpvar && (maxExpands > 0)) {
+ char *cpendvar = strchr(cpvar, ')');
+ if (cpendvar) {
+ int lenvar = cpendvar - cpvar - 2; // Subtract the $()
+ char *var = StringDup(cpvar + 2, lenvar);
+ SString val = GetWild(var, filename);
+ if (0 == strcmp(var, keybase))
+ val.clear(); // Self-references evaluate to empty string
+ size_t newlenbase = strlen(base) + val.length() - lenvar;
+ char *newbase = new char[newlenbase];
+ strncpy(newbase, base, cpvar - base);
+ strcpy(newbase + (cpvar - base), val.c_str());
+ strcpy(newbase + (cpvar - base) + val.length(), cpendvar + 1);
+ delete []var;
+ delete []base;
+ base = newbase;
+ }
+ cpvar = strstr(base, "$(");
+ maxExpands--;
+ }
+ SString sret = base;
+ delete []base;
+ return sret;
+}
+
+void PropSet::Clear() {
+ for (int root = 0; root < hashRoots; root++) {
+ Property *p = props[root];
+ while (p) {
+ Property *pNext = p->next;
+ p->hash = 0;
+ delete []p->key;
+ p->key = 0;
+ delete []p->val;
+ p->val = 0;
+ delete p;
+ p = pNext;
+ }
+ props[root] = 0;
+ }
+}
+
+char *PropSet::ToString() {
+ size_t len=0;
+ for (int r = 0; r < hashRoots; r++) {
+ for (Property *p = props[r]; p; p = p->next) {
+ len += strlen(p->key) + 1;
+ len += strlen(p->val) + 1;
+ }
+ }
+ if (len == 0)
+ len = 1; // Return as empty string
+ char *ret = new char [len];
+ if (ret) {
+ char *w = ret;
+ for (int root = 0; root < hashRoots; root++) {
+ for (Property *p = props[root]; p; p = p->next) {
+ strcpy(w, p->key);
+ w += strlen(p->key);
+ *w++ = '=';
+ strcpy(w, p->val);
+ w += strlen(p->val);
+ *w++ = '\n';
+ }
+ }
+ ret[len-1] = '\0';
+ }
+ return ret;
+}
+
+/**
+ * Initiate enumeration.
+ */
+bool PropSet::GetFirst(char **key, char **val) {
+ for (int i = 0; i < hashRoots; i++) {
+ for (Property *p = props[i]; p; p = p->next) {
+ if (p) {
+ *key = p->key;
+ *val = p->val;
+ enumnext = p->next; // GetNext will begin here ...
+ enumhash = i; // ... in this block
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/**
+ * Continue enumeration.
+ */
+bool PropSet::GetNext(char ** key, char ** val) {
+ bool firstloop = true;
+
+ // search begins where we left it : in enumhash block
+ for (int i = enumhash; i < hashRoots; i++) {
+ if (!firstloop)
+ enumnext = props[i]; // Begin with first property in block
+ // else : begin where we left
+ firstloop = false;
+
+ for (Property *p = enumnext; p; p = p->next) {
+ if (p) {
+ *key = p->key;
+ *val = p->val;
+ enumnext = p->next; // for GetNext
+ enumhash = i;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/**
+ * Creates an array that points into each word in the string and puts \0 terminators
+ * after each word.
+ */
+static char **ArrayFromWordList(char *wordlist, int *len, bool onlyLineEnds = false) {
+ int prev = '\n';
+ int words = 0;
+ // For rapid determination of whether a character is a separator, build
+ // a look up table.
+ bool wordSeparator[256];
+ for (int i=0;i<256; i++) {
+ wordSeparator[i] = false;
+ }
+ wordSeparator['\r'] = true;
+ wordSeparator['\n'] = true;
+ if (!onlyLineEnds) {
+ wordSeparator[' '] = true;
+ wordSeparator['\t'] = true;
+ }
+ for (int j = 0; wordlist[j]; j++) {
+ int curr = static_cast<unsigned char>(wordlist[j]);
+ if (!wordSeparator[curr] && wordSeparator[prev])
+ words++;
+ prev = curr;
+ }
+ char **keywords = new char *[words + 1];
+ if (keywords) {
+ words = 0;
+ prev = '\0';
+ size_t slen = strlen(wordlist);
+ for (size_t k = 0; k < slen; k++) {
+ if (!wordSeparator[static_cast<unsigned char>(wordlist[k])]) {
+ if (!prev) {
+ keywords[words] = &wordlist[k];
+ words++;
+ }
+ } else {
+ wordlist[k] = '\0';
+ }
+ prev = wordlist[k];
+ }
+ keywords[words] = &wordlist[slen];
+ *len = words;
+ } else {
+ *len = 0;
+ }
+ return keywords;
+}
+
+void WordList::Clear() {
+ if (words) {
+ delete []list;
+ delete []words;
+ delete []wordsNoCase;
+ }
+ words = 0;
+ wordsNoCase = 0;
+ list = 0;
+ len = 0;
+ sorted = false;
+ sortedNoCase = false;
+}
+
+void WordList::Set(const char *s) {
+ list = StringDup(s);
+ sorted = false;
+ sortedNoCase = false;
+ words = ArrayFromWordList(list, &len, onlyLineEnds);
+ wordsNoCase = new char * [len + 1];
+ memcpy(wordsNoCase, words, (len + 1) * sizeof (*words));
+}
+
+char *WordList::Allocate(int size) {
+ list = new char[size + 1];
+ list[size] = '\0';
+ return list;
+}
+
+void WordList::SetFromAllocated() {
+ sorted = false;
+ sortedNoCase = false;
+ words = ArrayFromWordList(list, &len, onlyLineEnds);
+ wordsNoCase = new char * [len + 1];
+ memcpy(wordsNoCase, words, (len + 1) * sizeof (*words));
+}
+
+extern "C" int cmpString(const void *a1, const void *a2) {
+ // Can't work out the correct incantation to use modern casts here
+ return strcmp(*(char**)(a1), *(char**)(a2));
+}
+
+extern "C" int cmpStringNoCase(const void *a1, const void *a2) {
+ // Can't work out the correct incantation to use modern casts here
+ return CompareCaseInsensitive(*(char**)(a1), *(char**)(a2));
+}
+
+static void SortWordList(char **words, unsigned int len) {
+ qsort(reinterpret_cast<void*>(words), len, sizeof(*words),
+ cmpString);
+}
+
+static void SortWordListNoCase(char **wordsNoCase, unsigned int len) {
+ qsort(reinterpret_cast<void*>(wordsNoCase), len, sizeof(*wordsNoCase),
+ cmpStringNoCase);
+}
+
+bool WordList::InList(const char *s) {
+ if (0 == words)
+ return false;
+ if (!sorted) {
+ sorted = true;
+ SortWordList(words, len);
+ for (unsigned int k = 0; k < (sizeof(starts) / sizeof(starts[0])); k++)
+ starts[k] = -1;
+ for (int l = len - 1; l >= 0; l--) {
+ unsigned char indexChar = words[l][0];
+ starts[indexChar] = l;
+ }
+ }
+ unsigned char firstChar = s[0];
+ int j = starts[firstChar];
+ if (j >= 0) {
+ while (words[j][0] == firstChar) {
+ 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['^'];
+ 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 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) {
+ if (0 == words)
+ return false;
+ if (!sorted) {
+ sorted = true;
+ SortWordList(words, len);
+ for (unsigned int k = 0; k < (sizeof(starts) / sizeof(starts[0])); k++)
+ starts[k] = -1;
+ for (int l = len - 1; l >= 0; l--) {
+ unsigned char indexChar = words[l][0];
+ starts[indexChar] = l;
+ }
+ }
+ unsigned char firstChar = s[0];
+ int j = starts[firstChar];
+ if (j >= 0) {
+ while (words[j][0] == firstChar) {
+ 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['^'];
+ 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;
+}
+
+/**
+ * Returns an element (complete) of the wordlist array which has
+ * the same beginning as the passed string.
+ * The length of the word to compare is passed too.
+ * Letter case can be ignored or preserved (default).
+ */
+const char *WordList::GetNearestWord(const char *wordStart, int searchLen, bool ignoreCase /*= false*/, SString wordCharacters /*='/0' */, int wordIndex /*= -1 */) {
+ int start = 0; // lower bound of the api array block to search
+ int end = len - 1; // upper bound of the api array block to search
+ int pivot; // index of api array element just being compared
+ int cond; // comparison result (in the sense of strcmp() result)
+ const char *word; // api array element just being compared
+
+ if (0 == words)
+ return NULL;
+ if (ignoreCase) {
+ if (!sortedNoCase) {
+ sortedNoCase = true;
+ SortWordListNoCase(wordsNoCase, len);
+ }
+ while (start <= end) { // binary searching loop
+ pivot = (start + end) >> 1;
+ word = wordsNoCase[pivot];
+ cond = CompareNCaseInsensitive(wordStart, word, searchLen);
+ if (!cond) {
+ // find first word
+ start = pivot;
+ while (start > 0 && !CompareNCaseInsensitive(wordStart, wordsNoCase[start-1], searchLen)) {
+ start--;
+ }
+ // find last word
+ end = pivot;
+ while (end < len-1 && !CompareNCaseInsensitive(wordStart, wordsNoCase[end+1], searchLen)) {
+ end++;
+ }
+
+ // Finds first word in a series of equal words
+ for (pivot = start; pivot <= end; pivot++) {
+ word = wordsNoCase[pivot];
+ if (!wordCharacters.contains(word[searchLen])) {
+ if (wordIndex <= 0) // Checks if a specific index was requested
+ return word; // result must not be freed with free()
+ wordIndex--;
+ }
+ }
+ return NULL;
+ }
+ else if (cond > 0)
+ start = pivot + 1;
+ else if (cond < 0)
+ end = pivot - 1;
+ }
+ } else { // preserve the letter case
+ if (!sorted) {
+ sorted = true;
+ SortWordList(words, len);
+ }
+ while (start <= end) { // binary searching loop
+ pivot = (start + end) >> 1;
+ word = words[pivot];
+ cond = strncmp(wordStart, word, searchLen);
+ if (!cond) {
+ // find first word
+ start = pivot;
+ while (start > 0 && !strncmp(wordStart, words[start-1], searchLen)) {
+ start--;
+ }
+ // find last word
+ end = pivot;
+ while (end < len-1 && !strncmp(wordStart, words[end+1], searchLen)) {
+ end++;
+ }
+
+ // Finds first word in a series of equal words
+ pivot = start;
+ while (pivot <= end) {
+ word = words[pivot];
+ if (!wordCharacters.contains(word[searchLen])) {
+ if (wordIndex <= 0) // Checks if a specific index was requested
+ return word; // result must not be freed with free()
+ wordIndex--;
+ }
+ pivot++;
+ }
+ return NULL;
+ }
+ else if (cond > 0)
+ start = pivot + 1;
+ else if (cond < 0)
+ end = pivot - 1;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Find the length of a 'word' which is actually an identifier in a string
+ * which looks like "identifier(..." or "identifier" and where
+ * there may be extra spaces after the identifier that should not be
+ * counted in the length.
+ */
+static unsigned int LengthWord(const char *word, char otherSeparator) {
+ // Find a '('. If that fails go to the end of the string.
+ const char *endWord = strchr(word, '(');
+ if (!endWord && otherSeparator)
+ endWord = strchr(word, otherSeparator);
+ if (!endWord)
+ endWord = word + strlen(word);
+ // Last case always succeeds so endWord != 0
+
+ // Drop any space characters.
+ if (endWord > word) {
+ endWord--; // Back from the '(', otherSeparator, or '\0'
+ // Move backwards over any spaces
+ while ((endWord > word) && (IsASpace(*endWord))) {
+ endWord--;
+ }
+ }
+ return endWord - word;
+}
+
+/**
+ * Returns elements (first words of them) of the wordlist array which have
+ * the same beginning as the passed string.
+ * The length of the word to compare is passed too.
+ * Letter case can be ignored or preserved (default).
+ * If there are more words meeting the condition they are returned all of
+ * them in the ascending order separated with spaces.
+ *
+ * NOTE: returned buffer has to be freed with delete[].
+ */
+char *WordList::GetNearestWords(
+ const char *wordStart,
+ int searchLen,
+ bool ignoreCase /*= false*/,
+ char otherSeparator /*= '\0'*/,
+ bool exactLen /*=false*/) {
+ unsigned int wordlen; // length of the word part (before the '(' brace) of the api array element
+ SString wordsNear;
+ wordsNear.setsizegrowth(1000);
+ int start = 0; // lower bound of the api array block to search
+ int end = len - 1; // upper bound of the api array block to search
+ int pivot; // index of api array element just being compared
+ int cond; // comparison result (in the sense of strcmp() result)
+
+ if (0 == words)
+ return NULL;
+ if (ignoreCase) {
+ if (!sortedNoCase) {
+ sortedNoCase = true;
+ SortWordListNoCase(wordsNoCase, len);
+ }
+ while (start <= end) { // Binary searching loop
+ pivot = (start + end) / 2;
+ cond = CompareNCaseInsensitive(wordStart, wordsNoCase[pivot], searchLen);
+ if (!cond) {
+ // Find first match
+ while ((pivot > start) &&
+ (0 == CompareNCaseInsensitive(wordStart,
+ wordsNoCase[pivot-1], searchLen))) {
+ --pivot;
+ }
+ // Grab each match
+ while ((pivot <= end) &&
+ (0 == CompareNCaseInsensitive(wordStart,
+ wordsNoCase[pivot], searchLen))) {
+ wordlen = LengthWord(wordsNoCase[pivot], otherSeparator) + 1;
+ ++pivot;
+ if (exactLen && wordlen != LengthWord(wordStart, otherSeparator) + 1)
+ continue;
+ wordsNear.append(wordsNoCase[pivot-1], wordlen, ' ');
+ }
+ return wordsNear.detach();
+ } else if (cond < 0) {
+ end = pivot - 1;
+ } else if (cond > 0) {
+ start = pivot + 1;
+ }
+ }
+ } else { // Preserve the letter case
+ if (!sorted) {
+ sorted = true;
+ SortWordList(words, len);
+ }
+ while (start <= end) { // Binary searching loop
+ pivot = (start + end) / 2;
+ cond = strncmp(wordStart, words[pivot], searchLen);
+ if (!cond) {
+ // Find first match
+ while ((pivot > start) &&
+ (0 == strncmp(wordStart,
+ words[pivot-1], searchLen))) {
+ --pivot;
+ }
+ // Grab each match
+ while ((pivot <= end) &&
+ (0 == strncmp(wordStart,
+ words[pivot], searchLen))) {
+ wordlen = LengthWord(words[pivot], otherSeparator) + 1;
+ ++pivot;
+ if (exactLen && wordlen != LengthWord(wordStart, otherSeparator) + 1)
+ continue;
+ wordsNear.append(words[pivot-1], wordlen, ' ');
+ }
+ return wordsNear.detach();
+ } else if (cond < 0) {
+ end = pivot - 1;
+ } else if (cond > 0) {
+ start = pivot + 1;
+ }
+ }
+ }
+ return NULL;
+}
diff --git a/src/RESearch.cpp b/src/RESearch.cpp
new file mode 100755
index 0000000..00e1830
--- /dev/null
+++ b/src/RESearch.cpp
@@ -0,0 +1,788 @@
+// Scintilla source code edit control
+/** @file RESearch.cxx
+ ** Regular expression search library.
+ **/
+
+/*
+ * regex - Regular expression pattern matching and replacement
+ *
+ * By: Ozan S. Yigit (oz)
+ * Dept. of Computer Science
+ * York University
+ *
+ * Original code available from http://www.cs.yorku.ca/~oz/
+ * Translation to C++ by Neil Hodgson neilh@scintilla.org
+ * Removed all use of register.
+ * Converted to modern function prototypes.
+ * Put all global/static variables into an object so this code can be
+ * used from multiple threads, etc.
+ *
+ * These routines are the PUBLIC DOMAIN equivalents of regex
+ * routines as found in 4.nBSD UN*X, with minor extensions.
+ *
+ * These routines are derived from various implementations found
+ * in software tools books, and Conroy's grep. They are NOT derived
+ * from licensed/restricted software.
+ * For more interesting/academic/complicated implementations,
+ * see Henry Spencer's regexp routines, or GNU Emacs pattern
+ * matching module.
+ *
+ * Modification history removed.
+ *
+ * Interfaces:
+ * RESearch::Compile: compile a regular expression into a NFA.
+ *
+ * const char *RESearch::Compile(const char *pat, int length,
+ * bool caseSensitive, bool posix)
+ *
+ * Returns a short error string if they fail.
+ *
+ * RESearch::Execute: execute the NFA to match a pattern.
+ *
+ * int RESearch::Execute(characterIndexer &ci, int lp, int endp)
+ *
+ * RESearch::Substitute: substitute the matched portions in a new string.
+ *
+ * int RESearch::Substitute(CharacterIndexer &ci, char *src, char *dst)
+ *
+ * re_fail: failure routine for RESearch::Execute. (no longer used)
+ *
+ * void re_fail(char *msg, char op)
+ *
+ * Regular Expressions:
+ *
+ * [1] char matches itself, unless it is a special
+ * character (metachar): . \ [ ] * + ^ $
+ * and ( ) if posix option.
+ *
+ * [2] . matches any character.
+ *
+ * [3] \ matches the character following it, except:
+ * - \a, \b, \f, \n, \t, \v match the
+ * corresponding C escape char;
+ * - if not in posix mode, when followed by a
+ * left or right round bracket (see [7]);
+ * - when followed by a digit 1 to 9 (see [8]);
+ * - when followed by a left or right angle bracket
+ * (see [9]).
+ * It is used as an escape character for all
+ * other meta-characters, and itself. When used
+ * in a set ([4]), it is treated as an ordinary
+ * character (except for escape chars).
+ *
+ * [4] [set] matches one of the characters in the set.
+ * If the first character in the set is "^",
+ * it matches a character NOT in the set, i.e.
+ * complements the set. A shorthand S-E (start-end)
+ * is used to specify a set of characters S upto
+ * E, inclusive. The special characters "]" and
+ * "-" have no special meaning if they appear
+ * as the first chars in the set. To include both,
+ * put - first: [-]A-Z]:
+ * [-]|] matches these 2 chars,
+ * []-|] matches from ] to | chars.
+ * examples: match:
+ *
+ * [a-z] any lowercase alpha
+ *
+ * [^-]] any char except - and ]
+ *
+ * [^A-Z] any char except uppercase
+ * alpha
+ *
+ * [a-zA-Z] any alpha
+ *
+ * [5] * any regular expression form [1] to [4], followed by
+ * closure char (*) matches zero or more matches of
+ * that form.
+ *
+ * [6] + same as [5], except it matches one or more.
+ *
+ * [7] a regular expression in the form [1] to [10], enclosed
+ * as \(form\) (or (form) with posix flag) matches what
+ * form matches. The enclosure creates a set of tags,
+ * used for [8] and for pattern substitution.
+ * The tagged forms are numbered starting from 1.
+ *
+ * [8] a \ followed by a digit 1 to 9 matches whatever a
+ * previously tagged regular expression ([7]) matched.
+ *
+ * [9] \< a regular expression starting with a \< construct
+ * \> and/or ending with a \> construct, restricts the
+ * pattern matching to the beginning of a word, and/or
+ * the end of a word. A word is defined to be a character
+ * string beginning and/or ending with the characters
+ * A-Z a-z 0-9 and _. It must also be preceded and/or
+ * followed by any character outside those mentioned.
+ *
+ * [10] a composite regular expression xy where x and y
+ * are in the form [1] to [10] matches the longest
+ * match of x followed by a match for y.
+ *
+ * [11] ^ a regular expression starting with a ^ character
+ * $ and/or ending with a $ character, restricts the
+ * pattern matching to the beginning of the line,
+ * or the end of line. [anchors] Elsewhere in the
+ * pattern, ^ and $ are treated as ordinary characters.
+ *
+ *
+ * Acknowledgements:
+ *
+ * HCR's Hugh Redelmeier has been most helpful in various
+ * stages of development. He convinced me to include BOW
+ * and EOW constructs, originally invented by Rob Pike at
+ * the University of Toronto.
+ *
+ * References:
+ * Software tools Kernighan & Plauger
+ * Software tools in Pascal Kernighan & Plauger
+ * Grep [rsx-11 C dist] David Conroy
+ * ed - text editor Un*x Programmer's Manual
+ * Advanced editing on Un*x B. W. Kernighan
+ * RegExp routines Henry Spencer
+ *
+ * Notes:
+ *
+ * This implementation uses a bit-set representation for character
+ * classes for speed and compactness. Each character is represented
+ * by one bit in a 256-bit block. Thus, CCL always takes a
+ * constant 32 bytes in the internal nfa, and RESearch::Execute does a single
+ * bit comparison to locate the character in the set.
+ *
+ * Examples:
+ *
+ * pattern: foo*.*
+ * compile: CHR f CHR o CLO CHR o END CLO ANY END END
+ * matches: fo foo fooo foobar fobar foxx ...
+ *
+ * pattern: fo[ob]a[rz]
+ * compile: CHR f CHR o CCL bitset CHR a CCL bitset END
+ * matches: fobar fooar fobaz fooaz
+ *
+ * pattern: foo\\+
+ * compile: CHR f CHR o CHR o CHR \ CLO CHR \ END END
+ * matches: foo\ foo\\ foo\\\ ...
+ *
+ * pattern: \(foo\)[1-3]\1 (same as foo[1-3]foo)
+ * compile: BOT 1 CHR f CHR o CHR o EOT 1 CCL bitset REF 1 END
+ * matches: foo1foo foo2foo foo3foo
+ *
+ * pattern: \(fo.*\)-\1
+ * compile: BOT 1 CHR f CHR o CLO ANY END EOT 1 CHR - REF 1 END
+ * matches: foo-foo fo-fo fob-fob foobar-foobar ...
+ */
+
+#include "CharClassify.h"
+#include "RESearch.h"
+
+// Shut up annoying Visual C++ warnings:
+#ifdef _MSC_VER
+#pragma warning(disable: 4514)
+#endif
+
+#define OKP 1
+#define NOP 0
+
+#define CHR 1
+#define ANY 2
+#define CCL 3
+#define BOL 4
+#define EOL 5
+#define BOT 6
+#define EOT 7
+#define BOW 8
+#define EOW 9
+#define REF 10
+#define CLO 11
+
+#define END 0
+
+/*
+ * The following defines are not meant to be changeable.
+ * They are for readability only.
+ */
+#define BLKIND 0370
+#define BITIND 07
+
+const char bitarr[] = {1,2,4,8,16,32,64,'\200'};
+
+#define badpat(x) (*nfa = END, x)
+
+/*
+ * Character classification table for word boundary operators BOW
+ * and EOW is passed in by the creator of this object (Scintilla
+ * Document). The Document default state is that word chars are:
+ * 0-9,a-z, A-Z and _
+ */
+
+RESearch::RESearch(CharClassify *charClassTable) {
+ charClass = charClassTable;
+ Init();
+}
+
+RESearch::~RESearch() {
+ Clear();
+}
+
+void RESearch::Init() {
+ sta = NOP; /* status of lastpat */
+ bol = 0;
+ for (int i=0; i<MAXTAG; i++)
+ pat[i] = 0;
+ for (int j=0; j<BITBLK; j++)
+ bittab[j] = 0;
+}
+
+void RESearch::Clear() {
+ for (int i=0; i<MAXTAG; i++) {
+ delete []pat[i];
+ pat[i] = 0;
+ bopat[i] = NOTFOUND;
+ eopat[i] = NOTFOUND;
+ }
+}
+
+bool RESearch::GrabMatches(CharacterIndexer &ci) {
+ bool success = true;
+ for (unsigned int i=0; i<MAXTAG; i++) {
+ if ((bopat[i] != NOTFOUND) && (eopat[i] != NOTFOUND)) {
+ unsigned int len = eopat[i] - bopat[i];
+ pat[i] = new char[len + 1];
+ if (pat[i]) {
+ for (unsigned int j=0; j<len; j++)
+ pat[i][j] = ci.CharAt(bopat[i] + j);
+ pat[i][len] = '\0';
+ } else {
+ success = false;
+ }
+ }
+ }
+ return success;
+}
+
+void RESearch::ChSet(char c) {
+ bittab[((c) & BLKIND) >> 3] |= bitarr[(c) & BITIND];
+}
+
+void RESearch::ChSetWithCase(char c, bool caseSensitive) {
+ if (caseSensitive) {
+ ChSet(c);
+ } else {
+ if ((c >= 'a') && (c <= 'z')) {
+ ChSet(c);
+ ChSet(static_cast<char>(c - 'a' + 'A'));
+ } else if ((c >= 'A') && (c <= 'Z')) {
+ ChSet(c);
+ ChSet(static_cast<char>(c - 'A' + 'a'));
+ } else {
+ ChSet(c);
+ }
+ }
+}
+
+const char escapeValue(char ch) {
+ switch (ch) {
+ case 'a': return '\a';
+ case 'b': return '\b';
+ case 'f': return '\f';
+ case 'n': return '\n';
+ case 'r': return '\r';
+ case 't': return '\t';
+ case 'v': return '\v';
+ }
+ return 0;
+}
+
+const char *RESearch::Compile(const char *pat, int length, bool caseSensitive, bool posix) {
+ char *mp=nfa; /* nfa pointer */
+ char *lp; /* saved pointer */
+ char *sp=nfa; /* another one */
+ char *mpMax = mp + MAXNFA - BITBLK - 10;
+
+ int tagi = 0; /* tag stack index */
+ int tagc = 1; /* actual tag count */
+
+ int n;
+ char mask; /* xor mask -CCL/NCL */
+ int c1, c2;
+
+ if (!pat || !length)
+ if (sta)
+ return 0;
+ else
+ return badpat("No previous regular expression");
+ sta = NOP;
+
+ const char *p=pat; /* pattern pointer */
+ for (int i=0; i<length; i++, p++) {
+ if (mp > mpMax)
+ return badpat("Pattern too long");
+ lp = mp;
+ switch(*p) {
+
+ case '.': /* match any char */
+ *mp++ = ANY;
+ break;
+
+ case '^': /* match beginning */
+ if (p == pat)
+ *mp++ = BOL;
+ else {
+ *mp++ = CHR;
+ *mp++ = *p;
+ }
+ break;
+
+ case '$': /* match endofline */
+ if (!*(p+1))
+ *mp++ = EOL;
+ else {
+ *mp++ = CHR;
+ *mp++ = *p;
+ }
+ break;
+
+ case '[': /* match char class */
+ *mp++ = CCL;
+
+ i++;
+ if (*++p == '^') {
+ mask = '\377';
+ i++;
+ p++;
+ } else
+ mask = 0;
+
+ if (*p == '-') { /* real dash */
+ i++;
+ ChSet(*p++);
+ }
+ if (*p == ']') { /* real brace */
+ i++;
+ ChSet(*p++);
+ }
+ while (*p && *p != ']') {
+ if (*p == '-' && *(p+1) && *(p+1) != ']') {
+ i++;
+ p++;
+ c1 = *(p-2) + 1;
+ i++;
+ c2 = *p++;
+ while (c1 <= c2) {
+ ChSetWithCase(static_cast<char>(c1++), caseSensitive);
+ }
+ } else if (*p == '\\' && *(p+1)) {
+ i++;
+ p++;
+ char escape = escapeValue(*p);
+ if (escape)
+ ChSetWithCase(escape, caseSensitive);
+ else
+ ChSetWithCase(*p, caseSensitive);
+ i++;
+ p++;
+ } else {
+ i++;
+ ChSetWithCase(*p++, caseSensitive);
+ }
+ }
+ if (!*p)
+ return badpat("Missing ]");
+
+ for (n = 0; n < BITBLK; bittab[n++] = (char) 0)
+ *mp++ = static_cast<char>(mask ^ bittab[n]);
+
+ break;
+
+ case '*': /* match 0 or more... */
+ case '+': /* match 1 or more... */
+ if (p == pat)
+ return badpat("Empty closure");
+ lp = sp; /* previous opcode */
+ if (*lp == CLO) /* equivalence... */
+ break;
+ switch(*lp) {
+
+ case BOL:
+ case BOT:
+ case EOT:
+ case BOW:
+ case EOW:
+ case REF:
+ return badpat("Illegal closure");
+ default:
+ break;
+ }
+
+ if (*p == '+')
+ for (sp = mp; lp < sp; lp++)
+ *mp++ = *lp;
+
+ *mp++ = END;
+ *mp++ = END;
+ sp = mp;
+ while (--mp > lp)
+ *mp = mp[-1];
+ *mp = CLO;
+ mp = sp;
+ break;
+
+ case '\\': /* tags, backrefs... */
+ i++;
+ switch(*++p) {
+
+ case '<':
+ *mp++ = BOW;
+ break;
+ case '>':
+ if (*sp == BOW)
+ return badpat("Null pattern inside \\<\\>");
+ *mp++ = EOW;
+ break;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ n = *p-'0';
+ if (tagi > 0 && tagstk[tagi] == n)
+ return badpat("Cyclical reference");
+ if (tagc > n) {
+ *mp++ = static_cast<char>(REF);
+ *mp++ = static_cast<char>(n);
+ }
+ else
+ return badpat("Undetermined reference");
+ break;
+ case 'a':
+ case 'b':
+ case 'n':
+ case 'f':
+ case 'r':
+ case 't':
+ case 'v':
+ *mp++ = CHR;
+ *mp++ = escapeValue(*p);
+ break;
+ default:
+ if (!posix && *p == '(') {
+ if (tagc < MAXTAG) {
+ tagstk[++tagi] = tagc;
+ *mp++ = BOT;
+ *mp++ = static_cast<char>(tagc++);
+ }
+ else
+ return badpat("Too many \\(\\) pairs");
+ } else if (!posix && *p == ')') {
+ if (*sp == BOT)
+ return badpat("Null pattern inside \\(\\)");
+ if (tagi > 0) {
+ *mp++ = static_cast<char>(EOT);
+ *mp++ = static_cast<char>(tagstk[tagi--]);
+ }
+ else
+ return badpat("Unmatched \\)");
+ } else {
+ *mp++ = CHR;
+ *mp++ = *p;
+ }
+ }
+ break;
+
+ default : /* an ordinary char */
+ if (posix && *p == '(') {
+ if (tagc < MAXTAG) {
+ tagstk[++tagi] = tagc;
+ *mp++ = BOT;
+ *mp++ = static_cast<char>(tagc++);
+ }
+ else
+ return badpat("Too many () pairs");
+ } else if (posix && *p == ')') {
+ if (*sp == BOT)
+ return badpat("Null pattern inside ()");
+ if (tagi > 0) {
+ *mp++ = static_cast<char>(EOT);
+ *mp++ = static_cast<char>(tagstk[tagi--]);
+ }
+ else
+ return badpat("Unmatched )");
+ } else if (caseSensitive) {
+ *mp++ = CHR;
+ *mp++ = *p;
+ } else {
+ *mp++ = CCL;
+ mask = 0;
+ ChSetWithCase(*p, false);
+ for (n = 0; n < BITBLK; bittab[n++] = (char) 0)
+ *mp++ = static_cast<char>(mask ^ bittab[n]);
+ }
+ break;
+ }
+ sp = lp;
+ }
+ if (tagi > 0)
+ return badpat((posix ? "Unmatched (" : "Unmatched \\("));
+ *mp = END;
+ sta = OKP;
+ return 0;
+}
+
+/*
+ * RESearch::Execute:
+ * execute nfa to find a match.
+ *
+ * special cases: (nfa[0])
+ * BOL
+ * Match only once, starting from the
+ * beginning.
+ * CHR
+ * First locate the character without
+ * calling PMatch, and if found, call
+ * PMatch for the remaining string.
+ * END
+ * RESearch::Compile failed, poor luser did not
+ * check for it. Fail fast.
+ *
+ * If a match is found, bopat[0] and eopat[0] are set
+ * to the beginning and the end of the matched fragment,
+ * respectively.
+ *
+ */
+
+int RESearch::Execute(CharacterIndexer &ci, int lp, int endp) {
+ char c;
+ int ep = NOTFOUND;
+ char *ap = nfa;
+
+ bol = lp;
+ failure = 0;
+
+ Clear();
+
+ switch(*ap) {
+
+ case BOL: /* anchored: match from BOL only */
+ ep = PMatch(ci, lp, endp, ap);
+ break;
+ case EOL: /* just searching for end of line normal path doesn't work */
+ if (*(ap+1) == END) {
+ lp = endp;
+ ep = lp;
+ break;
+ } else {
+ return 0;
+ }
+ case CHR: /* ordinary char: locate it fast */
+ c = *(ap+1);
+ while ((lp < endp) && (ci.CharAt(lp) != c))
+ lp++;
+ if (lp >= endp) /* if EOS, fail, else fall thru. */
+ return 0;
+ default: /* regular matching all the way. */
+ while (lp < endp) {
+ ep = PMatch(ci, lp, endp, ap);
+ if (ep != NOTFOUND)
+ break;
+ lp++;
+ }
+ break;
+ case END: /* munged automaton. fail always */
+ return 0;
+ }
+ if (ep == NOTFOUND)
+ return 0;
+
+ bopat[0] = lp;
+ eopat[0] = ep;
+ return 1;
+}
+
+/*
+ * PMatch: internal routine for the hard part
+ *
+ * This code is partly snarfed from an early grep written by
+ * David Conroy. The backref and tag stuff, and various other
+ * innovations are by oz.
+ *
+ * special case optimizations: (nfa[n], nfa[n+1])
+ * CLO ANY
+ * We KNOW .* will match everything upto the
+ * end of line. Thus, directly go to the end of
+ * line, without recursive PMatch calls. As in
+ * the other closure cases, the remaining pattern
+ * must be matched by moving backwards on the
+ * string recursively, to find a match for xy
+ * (x is ".*" and y is the remaining pattern)
+ * where the match satisfies the LONGEST match for
+ * x followed by a match for y.
+ * CLO CHR
+ * We can again scan the string forward for the
+ * single char and at the point of failure, we
+ * execute the remaining nfa recursively, same as
+ * above.
+ *
+ * At the end of a successful match, bopat[n] and eopat[n]
+ * are set to the beginning and end of subpatterns matched
+ * by tagged expressions (n = 1 to 9).
+ */
+
+extern void re_fail(char *,char);
+
+#define isinset(x,y) ((x)[((y)&BLKIND)>>3] & bitarr[(y)&BITIND])
+
+/*
+ * skip values for CLO XXX to skip past the closure
+ */
+
+#define ANYSKIP 2 /* [CLO] ANY END */
+#define CHRSKIP 3 /* [CLO] CHR chr END */
+#define CCLSKIP 34 /* [CLO] CCL 32 bytes END */
+
+int RESearch::PMatch(CharacterIndexer &ci, int lp, int endp, char *ap) {
+ int op, c, n;
+ int e; /* extra pointer for CLO */
+ int bp; /* beginning of subpat... */
+ int ep; /* ending of subpat... */
+ int are; /* to save the line ptr. */
+
+ while ((op = *ap++) != END)
+ switch(op) {
+
+ case CHR:
+ if (ci.CharAt(lp++) != *ap++)
+ return NOTFOUND;
+ break;
+ case ANY:
+ if (lp++ >= endp)
+ return NOTFOUND;
+ break;
+ case CCL:
+ c = ci.CharAt(lp++);
+ if (!isinset(ap,c))
+ return NOTFOUND;
+ ap += BITBLK;
+ break;
+ case BOL:
+ if (lp != bol)
+ return NOTFOUND;
+ break;
+ case EOL:
+ if (lp < endp)
+ return NOTFOUND;
+ break;
+ case BOT:
+ bopat[*ap++] = lp;
+ break;
+ case EOT:
+ eopat[*ap++] = lp;
+ break;
+ case BOW:
+ if (lp!=bol && iswordc(ci.CharAt(lp-1)) || !iswordc(ci.CharAt(lp)))
+ return NOTFOUND;
+ break;
+ case EOW:
+ if (lp==bol || !iswordc(ci.CharAt(lp-1)) || iswordc(ci.CharAt(lp)))
+ return NOTFOUND;
+ break;
+ case REF:
+ n = *ap++;
+ bp = bopat[n];
+ ep = eopat[n];
+ while (bp < ep)
+ if (ci.CharAt(bp++) != ci.CharAt(lp++))
+ return NOTFOUND;
+ break;
+ case CLO:
+ are = lp;
+ switch(*ap) {
+
+ case ANY:
+ while (lp < endp)
+ lp++;
+ n = ANYSKIP;
+ break;
+ case CHR:
+ c = *(ap+1);
+ while ((lp < endp) && (c == ci.CharAt(lp)))
+ lp++;
+ n = CHRSKIP;
+ break;
+ case CCL:
+ while ((lp < endp) && isinset(ap+1,ci.CharAt(lp)))
+ lp++;
+ n = CCLSKIP;
+ break;
+ default:
+ failure = true;
+ //re_fail("closure: bad nfa.", *ap);
+ return NOTFOUND;
+ }
+
+ ap += n;
+
+ while (lp >= are) {
+ if ((e = PMatch(ci, lp, endp, ap)) != NOTFOUND)
+ return e;
+ --lp;
+ }
+ return NOTFOUND;
+ default:
+ //re_fail("RESearch::Execute: bad nfa.", static_cast<char>(op));
+ return NOTFOUND;
+ }
+ return lp;
+}
+
+/*
+ * RESearch::Substitute:
+ * substitute the matched portions of the src in dst.
+ *
+ * & substitute the entire matched pattern.
+ *
+ * \digit substitute a subpattern, with the given tag number.
+ * Tags are numbered from 1 to 9. If the particular
+ * tagged subpattern does not exist, null is substituted.
+ */
+int RESearch::Substitute(CharacterIndexer &ci, char *src, char *dst) {
+ char c;
+ int pin;
+ int bp;
+ int ep;
+
+ if (!*src || !bopat[0])
+ return 0;
+
+ while ((c = *src++) != 0) {
+ switch(c) {
+
+ case '&':
+ pin = 0;
+ break;
+
+ case '\\':
+ c = *src++;
+ if (c >= '0' && c <= '9') {
+ pin = c - '0';
+ break;
+ }
+
+ default:
+ *dst++ = c;
+ continue;
+ }
+
+ if ((bp = bopat[pin]) != 0 && (ep = eopat[pin]) != 0) {
+ while (ci.CharAt(bp) && bp < ep)
+ *dst++ = ci.CharAt(bp++);
+ if (bp < ep)
+ return 0;
+ }
+ }
+ *dst = (char) 0;
+ return 1;
+}
diff --git a/src/RESearch.h b/src/RESearch.h
new file mode 100755
index 0000000..aa85579
--- /dev/null
+++ b/src/RESearch.h
@@ -0,0 +1,65 @@
+// Scintilla source code edit control
+/** @file RESearch.h
+ ** Interface to the regular expression search library.
+ **/
+// Written by Neil Hodgson <neilh@scintilla.org>
+// Based on the work of Ozan S. Yigit.
+// This file is in the public domain.
+
+#ifndef RESEARCH_H
+#define RESEARCH_H
+
+/*
+ * The following defines are not meant to be changeable.
+ * They are for readability only.
+ */
+#define MAXCHR 256
+#define CHRBIT 8
+#define BITBLK MAXCHR/CHRBIT
+
+class CharacterIndexer {
+public:
+ virtual char CharAt(int index)=0;
+ virtual ~CharacterIndexer() {
+ }
+};
+
+class RESearch {
+
+public:
+ RESearch(CharClassify *charClassTable);
+ ~RESearch();
+ bool GrabMatches(CharacterIndexer &ci);
+ const char *Compile(const char *pat, int length, bool caseSensitive, bool posix);
+ int Execute(CharacterIndexer &ci, int lp, int endp);
+ int Substitute(CharacterIndexer &ci, char *src, char *dst);
+
+ enum {MAXTAG=10};
+ enum {MAXNFA=2048};
+ enum {NOTFOUND=-1};
+
+ int bopat[MAXTAG];
+ int eopat[MAXTAG];
+ char *pat[MAXTAG];
+
+private:
+ void Init();
+ void Clear();
+ void ChSet(char c);
+ void ChSetWithCase(char c, bool caseSensitive);
+
+ int PMatch(CharacterIndexer &ci, int lp, int endp, char *ap);
+
+ int bol;
+ int tagstk[MAXTAG]; /* subpat tag stack */
+ char nfa[MAXNFA]; /* automaton */
+ int sta;
+ char bittab[BITBLK]; /* bit table for CCL pre-set bits */
+ int failure;
+ CharClassify *charClass;
+ bool iswordc(unsigned char x) {
+ return charClass->IsWord(x);
+ }
+};
+
+#endif
diff --git a/src/SVector.h b/src/SVector.h
new file mode 100755
index 0000000..c8edb51
--- /dev/null
+++ b/src/SVector.h
@@ -0,0 +1,127 @@
+// Scintilla source code edit control
+/** @file SVector.h
+ ** A simple expandable vector.
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@hare.net.au>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef SVECTOR_H
+#define SVECTOR_H
+
+/**
+ * A simple expandable integer vector.
+ * Storage not allocated for elements until an element is used.
+ * This makes it very lightweight unless used so is a good match for optional features.
+ */
+class SVector {
+ enum { allocSize = 4000 };
+
+ int *v; ///< The vector
+ unsigned int size; ///< Number of elements allocated
+ unsigned int len; ///< Number of elements used in vector
+ bool allocFailure; ///< A memory allocation call has failed
+
+ /** Internally allocate more elements than the user wants
+ * to avoid thrashing the memory allocator. */
+ void SizeTo(int newSize) {
+ if (newSize < allocSize)
+ newSize += allocSize;
+ else
+ newSize = (newSize * 3) / 2;
+ int* newv = new int[newSize];
+ if (!newv) {
+ allocFailure = true;
+ return;
+ }
+ size = newSize;
+ unsigned int i=0;
+ for (; i<len; i++) {
+ newv[i] = v[i];
+ }
+ for (; i<size; i++) {
+ newv[i] = 0;
+ }
+ delete []v;
+ v = newv;
+ }
+
+public:
+ SVector() {
+ allocFailure = false;
+ v = 0;
+ len = 0;
+ size = 0;
+ }
+ ~SVector() {
+ Free();
+ }
+ /// Constructor from another vector.
+ SVector(const SVector &other) {
+ allocFailure = false;
+ v = 0;
+ len = 0;
+ size = 0;
+ if (other.Length() > 0) {
+ SizeTo(other.Length());
+ if (!allocFailure) {
+ for (int i=0;i<other.Length();i++)
+ v[i] = other.v[i];
+ len = other.Length();
+ }
+ }
+ }
+ /// Copy constructor.
+ SVector &operator=(const SVector &other) {
+ if (this != &other) {
+ delete []v;
+ allocFailure = false;
+ v = 0;
+ len = 0;
+ size = 0;
+ if (other.Length() > 0) {
+ SizeTo(other.Length());
+ if (!allocFailure) {
+ for (int i=0;i<other.Length();i++)
+ v[i] = other.v[i];
+ }
+ len = other.Length();
+ }
+ }
+ return *this;
+ }
+ /** @brief Accessor.
+ * Allows to access values from the list, and grows it if accessing
+ * outside the current bounds. The returned value in this case is 0. */
+ int &operator[](unsigned int i) {
+ if (i >= len) {
+ if (i >= size) {
+ SizeTo(i);
+ }
+ len = i+1;
+ }
+ return v[i];
+ }
+ /// Reset vector.
+ void Free() {
+ delete []v;
+ v = 0;
+ size = 0;
+ len = 0;
+ }
+ /** @brief Grow vector size.
+ * Doesn't allow a vector to be shrinked. */
+ void SetLength(unsigned int newLength) {
+ if (newLength > len) {
+ if (newLength >= size) {
+ SizeTo(newLength);
+ }
+ }
+ len = newLength;
+ }
+ /// Get the current length (number of used elements) of the vector.
+ int Length() const {
+ return len;
+ }
+};
+
+#endif
diff --git a/src/SciTE.properties b/src/SciTE.properties
new file mode 100755
index 0000000..1a2bace
--- /dev/null
+++ b/src/SciTE.properties
@@ -0,0 +1,6 @@
+# SciTE.properties is the per directory local options file and can be used to override
+# settings made in SciTEGlobal.properties
+command.build.directory.*.cxx=..\win32
+command.build.directory.*.h=..\win32
+command.build.*.cxx=nmake -f scintilla.mak QUIET=1 DEBUG=1
+command.build.*.h=nmake -f scintilla.mak QUIET=1 DEBUG=1
diff --git a/src/ScintillaBase.cpp b/src/ScintillaBase.cpp
new file mode 100755
index 0000000..bcb0a77
--- /dev/null
+++ b/src/ScintillaBase.cpp
@@ -0,0 +1,727 @@
+// Scintilla source code edit control
+/** @file ScintillaBase.cxx
+ ** An enhanced subclass of Editor with calltips, autocomplete and context menu.
+ **/
+// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "Platform.h"
+
+#include "Scintilla.h"
+#include "PropSet.h"
+#ifdef SCI_LEXER
+#include "SciLexer.h"
+#include "Accessor.h"
+#include "DocumentAccessor.h"
+#include "KeyWords.h"
+#endif
+#include "ContractionState.h"
+#include "SVector.h"
+#include "CellBuffer.h"
+#include "CallTip.h"
+#include "KeyMap.h"
+#include "Indicator.h"
+#include "XPM.h"
+#include "LineMarker.h"
+#include "Style.h"
+#include "ViewStyle.h"
+#include "AutoComplete.h"
+#include "CharClassify.h"
+#include "Document.h"
+#include "Editor.h"
+#include "ScintillaBase.h"
+
+ScintillaBase::ScintillaBase() {
+ displayPopupMenu = true;
+ listType = 0;
+ maxListWidth = 0;
+#ifdef SCI_LEXER
+ lexLanguage = SCLEX_CONTAINER;
+ performingStyle = false;
+ lexCurrent = 0;
+ for (int wl = 0;wl < numWordLists;wl++)
+ keyWordLists[wl] = new WordList;
+ keyWordLists[numWordLists] = 0;
+#endif
+}
+
+ScintillaBase::~ScintillaBase() {
+#ifdef SCI_LEXER
+ for (int wl = 0;wl < numWordLists;wl++)
+ delete keyWordLists[wl];
+#endif
+}
+
+void ScintillaBase::Finalise() {
+ Editor::Finalise();
+ popup.Destroy();
+}
+
+void ScintillaBase::RefreshColourPalette(Palette &pal, bool want) {
+ Editor::RefreshColourPalette(pal, want);
+ ct.RefreshColourPalette(pal, want);
+}
+
+void ScintillaBase::AddCharUTF(char *s, unsigned int len, bool treatAsDBCS) {
+ bool isFillUp = ac.Active() && ac.IsFillUpChar(*s);
+ if (!isFillUp) {
+ Editor::AddCharUTF(s, len, treatAsDBCS);
+ }
+ if (ac.Active()) {
+ AutoCompleteCharacterAdded(s[0]);
+ // For fill ups add the character after the autocompletion has
+ // triggered so containers see the key so can display a calltip.
+ if (isFillUp) {
+ Editor::AddCharUTF(s, len, treatAsDBCS);
+ }
+ }
+}
+
+void ScintillaBase::Command(int cmdId) {
+
+ switch (cmdId) {
+
+ case idAutoComplete: // Nothing to do
+
+ break;
+
+ case idCallTip: // Nothing to do
+
+ break;
+
+ case idcmdUndo:
+ WndProc(SCI_UNDO, 0, 0);
+ break;
+
+ case idcmdRedo:
+ WndProc(SCI_REDO, 0, 0);
+ break;
+
+ case idcmdCut:
+ WndProc(SCI_CUT, 0, 0);
+ break;
+
+ case idcmdCopy:
+ WndProc(SCI_COPY, 0, 0);
+ break;
+
+ case idcmdPaste:
+ WndProc(SCI_PASTE, 0, 0);
+ break;
+
+ case idcmdDelete:
+ WndProc(SCI_CLEAR, 0, 0);
+ break;
+
+ case idcmdSelectAll:
+ WndProc(SCI_SELECTALL, 0, 0);
+ break;
+ }
+}
+
+int ScintillaBase::KeyCommand(unsigned int iMessage) {
+ // Most key commands cancel autocompletion mode
+ if (ac.Active()) {
+ switch (iMessage) {
+ // Except for these
+ case SCI_LINEDOWN:
+ AutoCompleteMove(1);
+ return 0;
+ case SCI_LINEUP:
+ AutoCompleteMove( -1);
+ return 0;
+ case SCI_PAGEDOWN:
+ AutoCompleteMove(5);
+ return 0;
+ case SCI_PAGEUP:
+ AutoCompleteMove( -5);
+ return 0;
+ case SCI_VCHOME:
+ AutoCompleteMove( -5000);
+ return 0;
+ case SCI_LINEEND:
+ AutoCompleteMove(5000);
+ return 0;
+ case SCI_DELETEBACK:
+ DelCharBack(true);
+ AutoCompleteCharacterDeleted();
+ EnsureCaretVisible();
+ return 0;
+ case SCI_DELETEBACKNOTLINE:
+ DelCharBack(false);
+ AutoCompleteCharacterDeleted();
+ EnsureCaretVisible();
+ return 0;
+ case SCI_TAB:
+ AutoCompleteCompleted();
+ return 0;
+ case SCI_NEWLINE:
+ AutoCompleteCompleted();
+ return 0;
+
+ default:
+ ac.Cancel();
+ }
+ }
+
+ if (ct.inCallTipMode) {
+ if (
+ (iMessage != SCI_CHARLEFT) &&
+ (iMessage != SCI_CHARLEFTEXTEND) &&
+ (iMessage != SCI_CHARRIGHT) &&
+ (iMessage != SCI_CHARLEFTEXTEND) &&
+ (iMessage != SCI_EDITTOGGLEOVERTYPE) &&
+ (iMessage != SCI_DELETEBACK) &&
+ (iMessage != SCI_DELETEBACKNOTLINE)
+ ) {
+ ct.CallTipCancel();
+ }
+ if ((iMessage == SCI_DELETEBACK) || (iMessage == SCI_DELETEBACKNOTLINE)) {
+ if (currentPos <= ct.posStartCallTip) {
+ ct.CallTipCancel();
+ }
+ }
+ }
+ return Editor::KeyCommand(iMessage);
+}
+
+void ScintillaBase::AutoCompleteDoubleClick(void* p) {
+ ScintillaBase* sci = reinterpret_cast<ScintillaBase*>(p);
+ sci->AutoCompleteCompleted();
+}
+
+void ScintillaBase::AutoCompleteStart(int lenEntered, const char *list) {
+ //Platform::DebugPrintf("AutoComplete %s\n", list);
+ ct.CallTipCancel();
+
+ if (ac.chooseSingle && (listType == 0)) {
+ if (list && !strchr(list, ac.GetSeparator())) {
+ const char *typeSep = strchr(list, ac.GetTypesep());
+ size_t lenInsert = (typeSep) ? (typeSep-list) : strlen(list);
+ if (ac.ignoreCase) {
+ SetEmptySelection(currentPos - lenEntered);
+ pdoc->DeleteChars(currentPos, lenEntered);
+ SetEmptySelection(currentPos);
+ pdoc->InsertString(currentPos, list, lenInsert);
+ SetEmptySelection(currentPos + lenInsert);
+ } else {
+ SetEmptySelection(currentPos);
+ pdoc->InsertString(currentPos, list + lenEntered, lenInsert - lenEntered);
+ SetEmptySelection(currentPos + lenInsert - lenEntered);
+ }
+ return;
+ }
+ }
+ ac.Start(wMain, idAutoComplete, currentPos, LocationFromPosition(currentPos),
+ lenEntered, vs.lineHeight, IsUnicodeMode());
+
+ PRectangle rcClient = GetClientRectangle();
+ Point pt = LocationFromPosition(currentPos - lenEntered);
+
+ int heightLB = 100;
+ int widthLB = 100;
+ if (pt.x >= rcClient.right - widthLB) {
+ HorizontalScrollTo(xOffset + pt.x - rcClient.right + widthLB);
+ Redraw();
+ pt = LocationFromPosition(currentPos);
+ }
+ PRectangle rcac;
+ rcac.left = pt.x - ac.lb->CaretFromEdge();
+ if (pt.y >= rcClient.bottom - heightLB && // Wont fit below.
+ pt.y >= (rcClient.bottom + rcClient.top) / 2) { // and there is more room above.
+ rcac.top = pt.y - heightLB;
+ if (rcac.top < 0) {
+ heightLB += rcac.top;
+ rcac.top = 0;
+ }
+ } else {
+ rcac.top = pt.y + vs.lineHeight;
+ }
+ rcac.right = rcac.left + widthLB;
+ rcac.bottom = Platform::Minimum(rcac.top + heightLB, rcClient.bottom);
+ ac.lb->SetPositionRelative(rcac, wMain);
+ ac.lb->SetFont(vs.styles[STYLE_DEFAULT].font);
+ unsigned int aveCharWidth = vs.styles[STYLE_DEFAULT].aveCharWidth;
+ ac.lb->SetAverageCharWidth(aveCharWidth);
+ ac.lb->SetDoubleClickAction(AutoCompleteDoubleClick, this);
+
+ ac.SetList(list);
+
+ // Fiddle the position of the list so it is right next to the target and wide enough for all its strings
+ PRectangle rcList = ac.lb->GetDesiredRect();
+ int heightAlloced = rcList.bottom - rcList.top;
+ widthLB = Platform::Maximum(widthLB, rcList.right - rcList.left);
+ if (maxListWidth != 0)
+ widthLB = Platform::Minimum(widthLB, aveCharWidth*maxListWidth);
+ // Make an allowance for large strings in list
+ rcList.left = pt.x - ac.lb->CaretFromEdge();
+ rcList.right = rcList.left + widthLB;
+ if (((pt.y + vs.lineHeight) >= (rcClient.bottom - heightAlloced)) && // Wont fit below.
+ ((pt.y + vs.lineHeight / 2) >= (rcClient.bottom + rcClient.top) / 2)) { // and there is more room above.
+ rcList.top = pt.y - heightAlloced;
+ } else {
+ rcList.top = pt.y + vs.lineHeight;
+ }
+ rcList.bottom = rcList.top + heightAlloced;
+ ac.lb->SetPositionRelative(rcList, wMain);
+ ac.Show(true);
+ if (lenEntered != 0) {
+ AutoCompleteMoveToCurrentWord();
+ }
+}
+
+void ScintillaBase::AutoCompleteCancel() {
+ ac.Cancel();
+}
+
+void ScintillaBase::AutoCompleteMove(int delta) {
+ ac.Move(delta);
+}
+
+void ScintillaBase::AutoCompleteMoveToCurrentWord() {
+ char wordCurrent[1000];
+ int i;
+ int startWord = ac.posStart - ac.startLen;
+ for (i = startWord; i < currentPos && i - startWord < 1000; i++)
+ wordCurrent[i - startWord] = pdoc->CharAt(i);
+ wordCurrent[Platform::Minimum(i - startWord, 999)] = '\0';
+ ac.Select(wordCurrent);
+}
+
+void ScintillaBase::AutoCompleteCharacterAdded(char ch) {
+ if (ac.IsFillUpChar(ch)) {
+ AutoCompleteCompleted();
+ } else if (ac.IsStopChar(ch)) {
+ ac.Cancel();
+ } else {
+ AutoCompleteMoveToCurrentWord();
+ }
+}
+
+void ScintillaBase::AutoCompleteCharacterDeleted() {
+ if (currentPos < ac.posStart - ac.startLen) {
+ ac.Cancel();
+ } else if (ac.cancelAtStartPos && (currentPos <= ac.posStart)) {
+ ac.Cancel();
+ } else {
+ AutoCompleteMoveToCurrentWord();
+ }
+}
+
+void ScintillaBase::AutoCompleteCompleted() {
+ int item = ac.lb->GetSelection();
+ char selected[1000];
+ selected[0] = '\0';
+ if (item != -1) {
+ ac.lb->GetValue(item, selected, sizeof(selected));
+ } else {
+ ac.Cancel();
+ return;
+ }
+
+ ac.Show(false);
+
+ listSelected = selected;
+ SCNotification scn = {0};
+ scn.nmhdr.code = listType > 0 ? SCN_USERLISTSELECTION : SCN_AUTOCSELECTION;
+ scn.message = 0;
+ scn.wParam = listType;
+ scn.listType = listType;
+ Position firstPos = ac.posStart - ac.startLen;
+ scn.lParam = firstPos;
+ scn.text = listSelected.c_str();
+ NotifyParent(scn);
+
+ if (!ac.Active())
+ return;
+ ac.Cancel();
+
+ if (listType > 0)
+ return;
+
+ Position endPos = currentPos;
+ if (ac.dropRestOfWord)
+ endPos = pdoc->ExtendWordSelect(endPos, 1, true);
+ if (endPos < firstPos)
+ return;
+ pdoc->BeginUndoAction();
+ if (endPos != firstPos) {
+ pdoc->DeleteChars(firstPos, endPos - firstPos);
+ }
+ SetEmptySelection(ac.posStart);
+ if (item != -1) {
+ SString piece = selected;
+ pdoc->InsertString(firstPos, piece.c_str());
+ SetEmptySelection(firstPos + static_cast<int>(piece.length()));
+ }
+ pdoc->EndUndoAction();
+}
+
+int ScintillaBase::AutoCompleteGetCurrent() {
+ return ac.lb->GetSelection();
+}
+
+void ScintillaBase::CallTipShow(Point pt, const char *defn) {
+ AutoCompleteCancel();
+ pt.y += vs.lineHeight;
+ // If container knows about STYLE_CALLTIP then use it in place of the
+ // STYLE_DEFAULT for the face name, size and character set. Also use it
+ // for the foreground and background colour.
+ int ctStyle = ct.UseStyleCallTip() ? STYLE_CALLTIP : STYLE_DEFAULT;
+ if (ct.UseStyleCallTip()) {
+ ct.SetForeBack(vs.styles[STYLE_CALLTIP].fore, vs.styles[STYLE_CALLTIP].back);
+ }
+ PRectangle rc = ct.CallTipStart(currentPos, pt,
+ defn,
+ vs.styles[ctStyle].fontName,
+ vs.styles[ctStyle].sizeZoomed,
+ CodePage(),
+ vs.styles[ctStyle].characterSet,
+ wMain);
+ // If the call-tip window would be out of the client
+ // space, adjust so it displays above the text.
+ PRectangle rcClient = GetClientRectangle();
+ if (rc.bottom > rcClient.bottom) {
+ int offset = vs.lineHeight + rc.Height();
+ rc.top -= offset;
+ rc.bottom -= offset;
+ }
+ // Now display the window.
+ CreateCallTipWindow(rc);
+ ct.wCallTip.SetPositionRelative(rc, wMain);
+ ct.wCallTip.Show();
+}
+
+void ScintillaBase::CallTipClick() {
+ SCNotification scn = {0};
+ scn.nmhdr.code = SCN_CALLTIPCLICK;
+ scn.position = ct.clickPlace;
+ NotifyParent(scn);
+}
+
+void ScintillaBase::ContextMenu(Point pt) {
+ if (displayPopupMenu) {
+ bool writable = !WndProc(SCI_GETREADONLY, 0, 0);
+ popup.CreatePopUp();
+ AddToPopUp("Undo", idcmdUndo, writable && pdoc->CanUndo());
+ AddToPopUp("Redo", idcmdRedo, writable && pdoc->CanRedo());
+ AddToPopUp("");
+ AddToPopUp("Cut", idcmdCut, writable && currentPos != anchor);
+ AddToPopUp("Copy", idcmdCopy, currentPos != anchor);
+ AddToPopUp("Paste", idcmdPaste, writable && WndProc(SCI_CANPASTE, 0, 0));
+ AddToPopUp("Delete", idcmdDelete, writable && currentPos != anchor);
+ AddToPopUp("");
+ AddToPopUp("Select All", idcmdSelectAll);
+ popup.Show(pt, wMain);
+ }
+}
+
+void ScintillaBase::CancelModes() {
+ AutoCompleteCancel();
+ ct.CallTipCancel();
+ Editor::CancelModes();
+}
+
+void ScintillaBase::ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt) {
+ CancelModes();
+ Editor::ButtonDown(pt, curTime, shift, ctrl, alt);
+}
+
+#ifdef SCI_LEXER
+void ScintillaBase::SetLexer(uptr_t wParam) {
+ lexLanguage = wParam;
+ lexCurrent = LexerModule::Find(lexLanguage);
+ if (!lexCurrent)
+ lexCurrent = LexerModule::Find(SCLEX_NULL);
+}
+
+void ScintillaBase::SetLexerLanguage(const char *languageName) {
+ lexLanguage = SCLEX_CONTAINER;
+ lexCurrent = LexerModule::Find(languageName);
+ if (!lexCurrent)
+ lexCurrent = LexerModule::Find(SCLEX_NULL);
+ if (lexCurrent)
+ lexLanguage = lexCurrent->GetLanguage();
+}
+
+void ScintillaBase::Colourise(int start, int end) {
+ if (!performingStyle) {
+ // Protect against reentrance, which may occur, for example, when
+ // fold points are discovered while performing styling and the folding
+ // code looks for child lines which may trigger styling.
+ performingStyle = true;
+
+ int lengthDoc = pdoc->Length();
+ if (end == -1)
+ end = lengthDoc;
+ int len = end - start;
+
+ PLATFORM_ASSERT(len >= 0);
+ PLATFORM_ASSERT(start + len <= lengthDoc);
+
+ //WindowAccessor styler(wMain.GetID(), props);
+ DocumentAccessor styler(pdoc, props, wMain.GetID());
+
+ int styleStart = 0;
+ if (start > 0)
+ styleStart = styler.StyleAt(start - 1) & pdoc->stylingBitsMask;
+ styler.SetCodePage(pdoc->dbcsCodePage);
+
+ if (lexCurrent && (len > 0)) { // Should always succeed as null lexer should always be available
+ lexCurrent->Lex(start, len, styleStart, keyWordLists, styler);
+ styler.Flush();
+ if (styler.GetPropertyInt("fold")) {
+ lexCurrent->Fold(start, len, styleStart, keyWordLists, styler);
+ styler.Flush();
+ }
+ }
+
+ performingStyle = false;
+ }
+}
+#endif
+
+void ScintillaBase::NotifyStyleToNeeded(int endStyleNeeded) {
+#ifdef SCI_LEXER
+ if (lexLanguage != SCLEX_CONTAINER) {
+ int endStyled = WndProc(SCI_GETENDSTYLED, 0, 0);
+ int lineEndStyled = WndProc(SCI_LINEFROMPOSITION, endStyled, 0);
+ endStyled = WndProc(SCI_POSITIONFROMLINE, lineEndStyled, 0);
+ Colourise(endStyled, endStyleNeeded);
+ return;
+ }
+#endif
+ Editor::NotifyStyleToNeeded(endStyleNeeded);
+}
+
+sptr_t ScintillaBase::WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) {
+ switch (iMessage) {
+ case SCI_AUTOCSHOW:
+ listType = 0;
+ AutoCompleteStart(wParam, reinterpret_cast<const char *>(lParam));
+ break;
+
+ case SCI_AUTOCCANCEL:
+ AutoCompleteCancel();
+ break;
+
+ case SCI_AUTOCACTIVE:
+ return ac.Active();
+
+ case SCI_AUTOCPOSSTART:
+ return ac.posStart;
+
+ case SCI_AUTOCCOMPLETE:
+ AutoCompleteCompleted();
+ break;
+
+ case SCI_AUTOCSETSEPARATOR:
+ ac.SetSeparator(static_cast<char>(wParam));
+ break;
+
+ case SCI_AUTOCGETSEPARATOR:
+ return ac.GetSeparator();
+
+ case SCI_AUTOCSTOPS:
+ ac.SetStopChars(reinterpret_cast<char *>(lParam));
+ break;
+
+ case SCI_AUTOCSELECT:
+ ac.Select(reinterpret_cast<char *>(lParam));
+ break;
+
+ case SCI_AUTOCGETCURRENT:
+ return AutoCompleteGetCurrent();
+
+ case SCI_AUTOCSETCANCELATSTART:
+ ac.cancelAtStartPos = wParam != 0;
+ break;
+
+ case SCI_AUTOCGETCANCELATSTART:
+ return ac.cancelAtStartPos;
+
+ case SCI_AUTOCSETFILLUPS:
+ ac.SetFillUpChars(reinterpret_cast<char *>(lParam));
+ break;
+
+ case SCI_AUTOCSETCHOOSESINGLE:
+ ac.chooseSingle = wParam != 0;
+ break;
+
+ case SCI_AUTOCGETCHOOSESINGLE:
+ return ac.chooseSingle;
+
+ case SCI_AUTOCSETIGNORECASE:
+ ac.ignoreCase = wParam != 0;
+ break;
+
+ case SCI_AUTOCGETIGNORECASE:
+ return ac.ignoreCase;
+
+ case SCI_USERLISTSHOW:
+ listType = wParam;
+ AutoCompleteStart(0, reinterpret_cast<const char *>(lParam));
+ break;
+
+ case SCI_AUTOCSETAUTOHIDE:
+ ac.autoHide = wParam != 0;
+ break;
+
+ case SCI_AUTOCGETAUTOHIDE:
+ return ac.autoHide;
+
+ case SCI_AUTOCSETDROPRESTOFWORD:
+ ac.dropRestOfWord = wParam != 0;
+ break;
+
+ case SCI_AUTOCGETDROPRESTOFWORD:
+ return ac.dropRestOfWord;
+
+ case SCI_AUTOCSETMAXHEIGHT:
+ ac.lb->SetVisibleRows(wParam);
+ break;
+
+ case SCI_AUTOCGETMAXHEIGHT:
+ return ac.lb->GetVisibleRows();
+
+ case SCI_AUTOCSETMAXWIDTH:
+ maxListWidth = wParam;
+ break;
+
+ case SCI_AUTOCGETMAXWIDTH:
+ return maxListWidth;
+
+ case SCI_REGISTERIMAGE:
+ ac.lb->RegisterImage(wParam, reinterpret_cast<const char *>(lParam));
+ break;
+
+ case SCI_CLEARREGISTEREDIMAGES:
+ ac.lb->ClearRegisteredImages();
+ break;
+
+ case SCI_AUTOCSETTYPESEPARATOR:
+ ac.SetTypesep(static_cast<char>(wParam));
+ break;
+
+ case SCI_AUTOCGETTYPESEPARATOR:
+ return ac.GetTypesep();
+
+ case SCI_CALLTIPSHOW:
+ CallTipShow(LocationFromPosition(wParam),
+ reinterpret_cast<const char *>(lParam));
+ break;
+
+ case SCI_CALLTIPCANCEL:
+ ct.CallTipCancel();
+ break;
+
+ case SCI_CALLTIPACTIVE:
+ return ct.inCallTipMode;
+
+ case SCI_CALLTIPPOSSTART:
+ return ct.posStartCallTip;
+
+ case SCI_CALLTIPSETHLT:
+ ct.SetHighlight(wParam, lParam);
+ break;
+
+ case SCI_CALLTIPSETBACK:
+ ct.colourBG = ColourDesired(wParam);
+ vs.styles[STYLE_CALLTIP].fore = ct.colourBG;
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_CALLTIPSETFORE:
+ ct.colourUnSel = ColourDesired(wParam);
+ vs.styles[STYLE_CALLTIP].fore = ct.colourUnSel;
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_CALLTIPSETFOREHLT:
+ ct.colourSel = ColourDesired(wParam);
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_CALLTIPUSESTYLE:
+ ct.SetTabSize((int)wParam);
+ InvalidateStyleRedraw();
+ break;
+
+ case SCI_USEPOPUP:
+ displayPopupMenu = wParam != 0;
+ break;
+
+#ifdef SCI_LEXER
+ case SCI_SETLEXER:
+ SetLexer(wParam);
+ lexLanguage = wParam;
+ break;
+
+ case SCI_GETLEXER:
+ return lexLanguage;
+
+ case SCI_COLOURISE:
+ if (lexLanguage == SCLEX_CONTAINER) {
+ pdoc->ModifiedAt(wParam);
+ NotifyStyleToNeeded((lParam == -1) ? pdoc->Length() : lParam);
+ } else {
+ Colourise(wParam, lParam);
+ }
+ Redraw();
+ break;
+
+ case SCI_SETPROPERTY:
+ props.Set(reinterpret_cast<const char *>(wParam),
+ reinterpret_cast<const char *>(lParam));
+ break;
+
+ case SCI_GETPROPERTY: {
+ SString val = props.Get(reinterpret_cast<const char *>(wParam));
+ const int n = val.length();
+ if (lParam != 0) {
+ char *ptr = reinterpret_cast<char *>(lParam);
+ memcpy(ptr, val.c_str(), n);
+ ptr[n] = '\0'; // terminate
+ }
+ return n; // Not including NUL
+ }
+
+ case SCI_GETPROPERTYEXPANDED: {
+ SString val = props.GetExpanded(reinterpret_cast<const char *>(wParam));
+ const int n = val.length();
+ if (lParam != 0) {
+ char *ptr = reinterpret_cast<char *>(lParam);
+ memcpy(ptr, val.c_str(), n);
+ ptr[n] = '\0'; // terminate
+ }
+ return n; // Not including NUL
+ }
+
+ case SCI_GETPROPERTYINT:
+ return props.GetInt(reinterpret_cast<const char *>(wParam), lParam);
+
+ case SCI_SETKEYWORDS:
+ if (wParam < numWordLists) {
+ keyWordLists[wParam]->Clear();
+ keyWordLists[wParam]->Set(reinterpret_cast<const char *>(lParam));
+ }
+ break;
+
+ case SCI_SETLEXERLANGUAGE:
+ SetLexerLanguage(reinterpret_cast<const char *>(lParam));
+ break;
+
+ case SCI_GETSTYLEBITSNEEDED:
+ return lexCurrent ? lexCurrent->GetStyleBitsNeeded() : 5;
+#endif
+
+ default:
+ return Editor::WndProc(iMessage, wParam, lParam);
+ }
+ return 0l;
+}
diff --git a/src/ScintillaBase.h b/src/ScintillaBase.h
new file mode 100755
index 0000000..cb85b55
--- /dev/null
+++ b/src/ScintillaBase.h
@@ -0,0 +1,93 @@
+// Scintilla source code edit control
+/** @file ScintillaBase.h
+ ** Defines an enhanced subclass of Editor with calltips, autocomplete and context menu.
+ **/
+// Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef SCINTILLABASE_H
+#define SCINTILLABASE_H
+
+/**
+ */
+class ScintillaBase : public Editor {
+ // Private so ScintillaBase objects can not be copied
+ ScintillaBase(const ScintillaBase &) : Editor() {}
+ ScintillaBase &operator=(const ScintillaBase &) { return *this; }
+
+protected:
+ /** Enumeration of commands and child windows. */
+ enum {
+ idCallTip=1,
+ idAutoComplete=2,
+
+ idcmdUndo=10,
+ idcmdRedo=11,
+ idcmdCut=12,
+ idcmdCopy=13,
+ idcmdPaste=14,
+ idcmdDelete=15,
+ idcmdSelectAll=16
+ };
+
+ bool displayPopupMenu;
+ Menu popup;
+ AutoComplete ac;
+
+ CallTip ct;
+
+ int listType; ///< 0 is an autocomplete list
+ SString listSelected; ///< Receives listbox selected string
+ int maxListWidth; /// Maximum width of list, in average character widths
+
+ bool performingStyle; ///< Prevent reentrance
+
+#ifdef SCI_LEXER
+ int lexLanguage;
+ const LexerModule *lexCurrent;
+ PropSet props;
+ enum {numWordLists=KEYWORDSET_MAX+1};
+ WordList *keyWordLists[numWordLists+1];
+ void SetLexer(uptr_t wParam);
+ void SetLexerLanguage(const char *languageName);
+ void Colourise(int start, int end);
+#endif
+
+ ScintillaBase();
+ virtual ~ScintillaBase();
+ virtual void Initialise() = 0;
+ virtual void Finalise() = 0;
+
+ virtual void RefreshColourPalette(Palette &pal, bool want);
+
+ virtual void AddCharUTF(char *s, unsigned int len, bool treatAsDBCS=false);
+ void Command(int cmdId);
+ virtual void CancelModes();
+ virtual int KeyCommand(unsigned int iMessage);
+
+ void AutoCompleteStart(int lenEntered, const char *list);
+ void AutoCompleteCancel();
+ void AutoCompleteMove(int delta);
+ int AutoCompleteGetCurrent();
+ void AutoCompleteCharacterAdded(char ch);
+ void AutoCompleteCharacterDeleted();
+ void AutoCompleteCompleted();
+ void AutoCompleteMoveToCurrentWord();
+ static void AutoCompleteDoubleClick(void* p);
+
+ void CallTipClick();
+ void CallTipShow(Point pt, const char *defn);
+ virtual void CreateCallTipWindow(PRectangle rc) = 0;
+
+ virtual void AddToPopUp(const char *label, int cmd=0, bool enabled=true) = 0;
+ void ContextMenu(Point pt);
+
+ virtual void ButtonDown(Point pt, unsigned int curTime, bool shift, bool ctrl, bool alt);
+
+ virtual void NotifyStyleToNeeded(int endStyleNeeded);
+public:
+ // Public so scintilla_send_message can use it
+ virtual sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
+};
+
+#endif
diff --git a/src/Style.cpp b/src/Style.cpp
new file mode 100755
index 0000000..f01aee0
--- /dev/null
+++ b/src/Style.cpp
@@ -0,0 +1,154 @@
+// Scintilla source code edit control
+/** @file Style.cxx
+ ** Defines the font and colour style for a class of text.
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <string.h>
+
+#include "Platform.h"
+
+#include "Scintilla.h"
+#include "Style.h"
+
+Style::Style() {
+ aliasOfDefaultFont = true;
+ Clear(ColourDesired(0, 0, 0), ColourDesired(0xff, 0xff, 0xff),
+ Platform::DefaultFontSize(), 0, SC_CHARSET_DEFAULT,
+ false, false, false, false, caseMixed, true, true, false);
+}
+
+Style::Style(const Style &source) {
+ Clear(ColourDesired(0, 0, 0), ColourDesired(0xff, 0xff, 0xff),
+ 0, 0, 0,
+ false, false, false, false, caseMixed, true, true, false);
+ fore.desired = source.fore.desired;
+ back.desired = source.back.desired;
+ characterSet = source.characterSet;
+ bold = source.bold;
+ italic = source.italic;
+ size = source.size;
+ eolFilled = source.eolFilled;
+ underline = source.underline;
+ caseForce = source.caseForce;
+ visible = source.visible;
+ changeable = source.changeable;
+ hotspot = source.hotspot;
+}
+
+Style::~Style() {
+ if (aliasOfDefaultFont)
+ font.SetID(0);
+ else
+ font.Release();
+ aliasOfDefaultFont = false;
+}
+
+Style &Style::operator=(const Style &source) {
+ if (this == &source)
+ return * this;
+ Clear(ColourDesired(0, 0, 0), ColourDesired(0xff, 0xff, 0xff),
+ 0, 0, SC_CHARSET_DEFAULT,
+ false, false, false, false, caseMixed, true, true, false);
+ fore.desired = source.fore.desired;
+ back.desired = source.back.desired;
+ characterSet = source.characterSet;
+ bold = source.bold;
+ italic = source.italic;
+ size = source.size;
+ eolFilled = source.eolFilled;
+ underline = source.underline;
+ caseForce = source.caseForce;
+ visible = source.visible;
+ changeable = source.changeable;
+ return *this;
+}
+
+void Style::Clear(ColourDesired fore_, ColourDesired back_, int size_,
+ const char *fontName_, int characterSet_,
+ bool bold_, bool italic_, bool eolFilled_,
+ bool underline_, ecaseForced caseForce_,
+ bool visible_, bool changeable_, bool hotspot_) {
+ fore.desired = fore_;
+ back.desired = back_;
+ characterSet = characterSet_;
+ bold = bold_;
+ italic = italic_;
+ size = size_;
+ fontName = fontName_;
+ eolFilled = eolFilled_;
+ underline = underline_;
+ caseForce = caseForce_;
+ visible = visible_;
+ changeable = changeable_;
+ hotspot = hotspot_;
+ if (aliasOfDefaultFont)
+ font.SetID(0);
+ else
+ font.Release();
+ aliasOfDefaultFont = false;
+}
+
+void Style::ClearTo(const Style &source) {
+ Clear(
+ source.fore.desired,
+ source.back.desired,
+ source.size,
+ source.fontName,
+ source.characterSet,
+ source.bold,
+ source.italic,
+ source.eolFilled,
+ source.underline,
+ source.caseForce,
+ source.visible,
+ source.changeable,
+ source.hotspot);
+}
+
+bool Style::EquivalentFontTo(const Style *other) const {
+ if (bold != other->bold ||
+ italic != other->italic ||
+ size != other->size ||
+ characterSet != other->characterSet)
+ return false;
+ if (fontName == other->fontName)
+ return true;
+ if (!fontName)
+ return false;
+ if (!other->fontName)
+ return false;
+ return strcmp(fontName, other->fontName) == 0;
+}
+
+void Style::Realise(Surface &surface, int zoomLevel, Style *defaultStyle, bool extraFontFlag) {
+ sizeZoomed = size + zoomLevel;
+ if (sizeZoomed <= 2) // Hangs if sizeZoomed <= 1
+ sizeZoomed = 2;
+
+ if (aliasOfDefaultFont)
+ font.SetID(0);
+ else
+ font.Release();
+ int deviceHeight = surface.DeviceHeightFont(sizeZoomed);
+ aliasOfDefaultFont = defaultStyle &&
+ (EquivalentFontTo(defaultStyle) || !fontName);
+ if (aliasOfDefaultFont) {
+ font.SetID(defaultStyle->font.GetID());
+ } else if (fontName) {
+ font.Create(fontName, characterSet, deviceHeight, bold, italic, extraFontFlag);
+ } else {
+ font.SetID(0);
+ }
+
+ ascent = surface.Ascent(font);
+ descent = surface.Descent(font);
+ // Probably more typographically correct to include leading
+ // but that means more complex drawing as leading must be erased
+ //lineHeight = surface.ExternalLeading() + surface.Height();
+ externalLeading = surface.ExternalLeading(font);
+ lineHeight = surface.Height(font);
+ aveCharWidth = surface.AverageCharWidth(font);
+ spaceWidth = surface.WidthChar(font, ' ');
+}
diff --git a/src/Style.h b/src/Style.h
new file mode 100755
index 0000000..c0f7eca
--- /dev/null
+++ b/src/Style.h
@@ -0,0 +1,56 @@
+// Scintilla source code edit control
+/** @file Style.h
+ ** Defines the font and colour style for a class of text.
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef STYLE_H
+#define STYLE_H
+
+/**
+ */
+class Style {
+public:
+ ColourPair fore;
+ ColourPair back;
+ bool aliasOfDefaultFont;
+ bool bold;
+ bool italic;
+ int size;
+ const char *fontName;
+ int characterSet;
+ bool eolFilled;
+ bool underline;
+ enum ecaseForced {caseMixed, caseUpper, caseLower};
+ ecaseForced caseForce;
+ bool visible;
+ bool changeable;
+ bool hotspot;
+
+ Font font;
+ int sizeZoomed;
+ unsigned int lineHeight;
+ unsigned int ascent;
+ unsigned int descent;
+ unsigned int externalLeading;
+ unsigned int aveCharWidth;
+ unsigned int spaceWidth;
+
+ Style();
+ Style(const Style &source);
+ ~Style();
+ Style &operator=(const Style &source);
+ void Clear(ColourDesired fore_, ColourDesired back_,
+ int size_,
+ const char *fontName_, int characterSet_,
+ bool bold_, bool italic_, bool eolFilled_,
+ bool underline_, ecaseForced caseForce_,
+ bool visible_, bool changeable_, bool hotspot_);
+ void ClearTo(const Style &source);
+ bool EquivalentFontTo(const Style *other) const;
+ void Realise(Surface &surface, int zoomLevel, Style *defaultStyle = 0, bool extraFontFlag = false);
+ bool IsProtected() const { return !(changeable && visible);};
+};
+
+#endif
diff --git a/src/StyleContext.cpp b/src/StyleContext.cpp
new file mode 100755
index 0000000..d9da0ed
--- /dev/null
+++ b/src/StyleContext.cpp
@@ -0,0 +1,51 @@
+// 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 <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "StyleContext.h"
+
+static void getRange(unsigned int start,
+ unsigned int end,
+ Accessor &styler,
+ char *s,
+ unsigned int len) {
+ unsigned int i = 0;
+ while ((i < end - start + 1) && (i < len-1)) {
+ s[i] = styler[start + i];
+ i++;
+ }
+ s[i] = '\0';
+}
+
+void StyleContext::GetCurrent(char *s, unsigned int len) {
+ getRange(styler.GetStartSegment(), currentPos - 1, styler, s, len);
+}
+
+static void getRangeLowered(unsigned int start,
+ unsigned int end,
+ Accessor &styler,
+ char *s,
+ unsigned int len) {
+ unsigned int i = 0;
+ while ((i < end - start + 1) && (i < len-1)) {
+ s[i] = static_cast<char>(tolower(styler[start + i]));
+ i++;
+ }
+ s[i] = '\0';
+}
+
+void StyleContext::GetCurrentLowered(char *s, unsigned int len) {
+ getRangeLowered(styler.GetStartSegment(), currentPos - 1, styler, s, len);
+}
diff --git a/src/StyleContext.h b/src/StyleContext.h
new file mode 100755
index 0000000..75e1878
--- /dev/null
+++ b/src/StyleContext.h
@@ -0,0 +1,169 @@
+// 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.
+
+// 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 {
+ Accessor &styler;
+ unsigned int endPos;
+ StyleContext& operator=(const StyleContext&) {
+ return *this;
+ }
+ void GetNextChar(unsigned int pos) {
+ chNext = static_cast<unsigned char>(styler.SafeGetCharAt(pos+1));
+ if (styler.IsLeadByte(static_cast<char>(chNext))) {
+ chNext = chNext << 8;
+ chNext |= static_cast<unsigned char>(styler.SafeGetCharAt(pos+2));
+ }
+ // End of line?
+ // Trigger on CR only (Mac style) or either on LF from CR+LF (Dos/Win)
+ // or on LF alone (Unix). Avoid triggering two times on Dos/Win.
+ atLineEnd = (ch == '\r' && chNext != '\n') ||
+ (ch == '\n') ||
+ (currentPos >= endPos);
+ }
+
+public:
+ unsigned int currentPos;
+ bool atLineStart;
+ bool atLineEnd;
+ int state;
+ int chPrev;
+ int ch;
+ int chNext;
+
+ StyleContext(unsigned int startPos, unsigned int length,
+ int initStyle, Accessor &styler_, char chMask=31) :
+ styler(styler_),
+ endPos(startPos + length),
+ currentPos(startPos),
+ atLineStart(true),
+ atLineEnd(false),
+ state(initStyle & chMask), // Mask off all bits which aren't in the chMask.
+ chPrev(0),
+ ch(0),
+ chNext(0) {
+ styler.StartAt(startPos, chMask);
+ styler.StartSegment(startPos);
+ unsigned int pos = currentPos;
+ ch = static_cast<unsigned char>(styler.SafeGetCharAt(pos));
+ if (styler.IsLeadByte(static_cast<char>(ch))) {
+ pos++;
+ ch = ch << 8;
+ ch |= static_cast<unsigned char>(styler.SafeGetCharAt(pos));
+ }
+ GetNextChar(pos);
+ }
+ void Complete() {
+ styler.ColourTo(currentPos - 1, state);
+ }
+ bool More() {
+ return currentPos < endPos;
+ }
+ void Forward() {
+ if (currentPos < endPos) {
+ atLineStart = atLineEnd;
+ chPrev = ch;
+ currentPos++;
+ if (ch >= 0x100)
+ currentPos++;
+ ch = chNext;
+ GetNextChar(currentPos + ((ch >= 0x100) ? 1 : 0));
+ } else {
+ atLineStart = false;
+ chPrev = ' ';
+ ch = ' ';
+ chNext = ' ';
+ atLineEnd = true;
+ }
+ }
+ void Forward(int nb) {
+ for (int i = 0; i < nb; i++) {
+ Forward();
+ }
+ }
+ void ChangeState(int state_) {
+ state = state_;
+ }
+ void SetState(int state_) {
+ styler.ColourTo(currentPos - 1, state);
+ state = state_;
+ }
+ void ForwardSetState(int state_) {
+ Forward();
+ styler.ColourTo(currentPos - 1, state);
+ state = state_;
+ }
+ int LengthCurrent() {
+ return currentPos - styler.GetStartSegment();
+ }
+ int GetRelative(int n) {
+ return static_cast<unsigned char>(styler.SafeGetCharAt(currentPos+n));
+ }
+ bool Match(char ch0) {
+ return ch == static_cast<unsigned char>(ch0);
+ }
+ bool Match(char ch0, char ch1) {
+ return (ch == static_cast<unsigned char>(ch0)) && (chNext == static_cast<unsigned char>(ch1));
+ }
+ bool Match(const char *s) {
+ if (ch != static_cast<unsigned char>(*s))
+ return false;
+ s++;
+ if (chNext != static_cast<unsigned char>(*s))
+ return false;
+ s++;
+ for (int n=2; *s; n++) {
+ if (*s != styler.SafeGetCharAt(currentPos+n))
+ return false;
+ s++;
+ }
+ return true;
+ }
+ bool MatchIgnoreCase(const char *s) {
+ if (tolower(ch) != static_cast<unsigned char>(*s))
+ return false;
+ s++;
+ if (tolower(chNext) != static_cast<unsigned char>(*s))
+ return false;
+ s++;
+ for (int n=2; *s; n++) {
+ if (static_cast<unsigned char>(*s) !=
+ tolower(static_cast<unsigned char>(styler.SafeGetCharAt(currentPos+n))))
+ return false;
+ s++;
+ }
+ return true;
+ }
+ // Non-inline
+ void GetCurrent(char *s, unsigned int len);
+ void GetCurrentLowered(char *s, unsigned int len);
+};
+
+inline bool IsASpace(unsigned int ch) {
+ return (ch == ' ') || ((ch >= 0x09) && (ch <= 0x0d));
+}
+
+inline bool IsASpaceOrTab(unsigned int ch) {
+ return (ch == ' ') || (ch == '\t');
+}
+
+inline bool IsADigit(unsigned int ch) {
+ return (ch >= '0') && (ch <= '9');
+}
+
+inline bool IsADigit(unsigned int ch, unsigned int base) {
+ 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));
+ }
+}
diff --git a/src/UniConversion.cpp b/src/UniConversion.cpp
new file mode 100755
index 0000000..363db90
--- /dev/null
+++ b/src/UniConversion.cpp
@@ -0,0 +1,76 @@
+// Scintilla source code edit control
+/** @file UniConversion.cxx
+ ** Functions to handle UFT-8 and UCS-2 strings.
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+
+#include "UniConversion.h"
+
+unsigned int UTF8Length(const wchar_t *uptr, unsigned int tlen) {
+ unsigned int len = 0;
+ for (unsigned int i = 0; i < tlen && uptr[i]; i++) {
+ unsigned int uch = uptr[i];
+ if (uch < 0x80)
+ len++;
+ else if (uch < 0x800)
+ len += 2;
+ else
+ len +=3;
+ }
+ return len;
+}
+
+void UTF8FromUCS2(const wchar_t *uptr, unsigned int tlen, char *putf, unsigned int len) {
+ int k = 0;
+ for (unsigned int i = 0; i < tlen && uptr[i]; i++) {
+ unsigned int uch = uptr[i];
+ if (uch < 0x80) {
+ putf[k++] = static_cast<char>(uch);
+ } else if (uch < 0x800) {
+ putf[k++] = static_cast<char>(0xC0 | (uch >> 6));
+ putf[k++] = static_cast<char>(0x80 | (uch & 0x3f));
+ } else {
+ putf[k++] = static_cast<char>(0xE0 | (uch >> 12));
+ putf[k++] = static_cast<char>(0x80 | ((uch >> 6) & 0x3f));
+ putf[k++] = static_cast<char>(0x80 | (uch & 0x3f));
+ }
+ }
+ putf[len] = '\0';
+}
+
+unsigned int UCS2Length(const char *s, unsigned int len) {
+ unsigned int ulen = 0;
+ for (unsigned int i=0;i<len;i++) {
+ unsigned char ch = static_cast<unsigned char>(s[i]);
+ if ((ch < 0x80) || (ch > (0x80 + 0x40)))
+ ulen++;
+ }
+ return ulen;
+}
+
+unsigned int UCS2FromUTF8(const char *s, unsigned int len, wchar_t *tbuf, unsigned int tlen) {
+ unsigned int ui=0;
+ const unsigned char *us = reinterpret_cast<const unsigned char *>(s);
+ unsigned int i=0;
+ while ((i<len) && (ui<tlen)) {
+ unsigned char ch = us[i++];
+ if (ch < 0x80) {
+ tbuf[ui] = ch;
+ } else if (ch < 0x80 + 0x40 + 0x20) {
+ tbuf[ui] = static_cast<wchar_t>((ch & 0x1F) << 6);
+ ch = us[i++];
+ tbuf[ui] = static_cast<wchar_t>(tbuf[ui] + (ch & 0x7F));
+ } else {
+ tbuf[ui] = static_cast<wchar_t>((ch & 0xF) << 12);
+ ch = us[i++];
+ tbuf[ui] = static_cast<wchar_t>(tbuf[ui] + ((ch & 0x7F) << 6));
+ ch = us[i++];
+ tbuf[ui] = static_cast<wchar_t>(tbuf[ui] + (ch & 0x7F));
+ }
+ ui++;
+ }
+ return ui;
+}
diff --git a/src/UniConversion.h b/src/UniConversion.h
new file mode 100755
index 0000000..bd1d775
--- /dev/null
+++ b/src/UniConversion.h
@@ -0,0 +1,12 @@
+// Scintilla source code edit control
+/** @file UniConversion.h
+ ** Functions to handle UFT-8 and UCS-2 strings.
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+unsigned int UTF8Length(const wchar_t *uptr, unsigned int tlen);
+void UTF8FromUCS2(const wchar_t *uptr, unsigned int tlen, char *putf, unsigned int len);
+unsigned int UCS2Length(const char *s, unsigned int len);
+unsigned int UCS2FromUTF8(const char *s, unsigned int len, wchar_t *tbuf, unsigned int tlen);
+
diff --git a/src/ViewStyle.cpp b/src/ViewStyle.cpp
new file mode 100755
index 0000000..b4da30a
--- /dev/null
+++ b/src/ViewStyle.cpp
@@ -0,0 +1,297 @@
+// Scintilla source code edit control
+/** @file ViewStyle.cxx
+ ** Store information on how the document is to be viewed.
+ **/
+// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <string.h>
+
+#include "Platform.h"
+
+#include "Scintilla.h"
+#include "Indicator.h"
+#include "XPM.h"
+#include "LineMarker.h"
+#include "Style.h"
+#include "ViewStyle.h"
+
+MarginStyle::MarginStyle() :
+ style(SC_MARGIN_SYMBOL), width(0), mask(0), sensitive(false) {
+}
+
+// A list of the fontnames - avoids wasting space in each style
+FontNames::FontNames() {
+ max = 0;
+}
+
+FontNames::~FontNames() {
+ Clear();
+}
+
+void FontNames::Clear() {
+ for (int i=0;i<max;i++) {
+ delete []names[i];
+ }
+ max = 0;
+}
+
+const char *FontNames::Save(const char *name) {
+ if (!name)
+ return 0;
+ for (int i=0;i<max;i++) {
+ if (strcmp(names[i], name) == 0) {
+ return names[i];
+ }
+ }
+ names[max] = new char[strlen(name) + 1];
+ strcpy(names[max], name);
+ max++;
+ return names[max-1];
+}
+
+ViewStyle::ViewStyle() {
+ Init();
+}
+
+ViewStyle::ViewStyle(const ViewStyle &source) {
+ Init();
+ for (unsigned int sty=0;sty<(sizeof(styles)/sizeof(styles[0]));sty++) {
+ styles[sty] = source.styles[sty];
+ // Can't just copy fontname as its lifetime is relative to its owning ViewStyle
+ styles[sty].fontName = fontNames.Save(source.styles[sty].fontName);
+ }
+ for (int mrk=0;mrk<=MARKER_MAX;mrk++) {
+ markers[mrk] = source.markers[mrk];
+ }
+ for (int ind=0;ind<=INDIC_MAX;ind++) {
+ indicators[ind] = source.indicators[ind];
+ }
+
+ selforeset = source.selforeset;
+ selforeground.desired = source.selforeground.desired;
+ selbackset = source.selbackset;
+ selbackground.desired = source.selbackground.desired;
+ selbackground2.desired = source.selbackground2.desired;
+ selAlpha = source.selAlpha;
+
+ foldmarginColourSet = source.foldmarginColourSet;
+ foldmarginColour.desired = source.foldmarginColour.desired;
+ foldmarginHighlightColourSet = source.foldmarginHighlightColourSet;
+ foldmarginHighlightColour.desired = source.foldmarginHighlightColour.desired;
+
+ hotspotForegroundSet = source.hotspotForegroundSet;
+ hotspotForeground.desired = source.hotspotForeground.desired;
+ hotspotBackgroundSet = source.hotspotBackgroundSet;
+ hotspotBackground.desired = source.hotspotBackground.desired;
+ hotspotUnderline = source.hotspotUnderline;
+ hotspotSingleLine = source.hotspotSingleLine;
+
+ whitespaceForegroundSet = source.whitespaceForegroundSet;
+ whitespaceForeground.desired = source.whitespaceForeground.desired;
+ whitespaceBackgroundSet = source.whitespaceBackgroundSet;
+ whitespaceBackground.desired = source.whitespaceBackground.desired;
+ selbar.desired = source.selbar.desired;
+ selbarlight.desired = source.selbarlight.desired;
+ caretcolour.desired = source.caretcolour.desired;
+ showCaretLineBackground = source.showCaretLineBackground;
+ caretLineBackground.desired = source.caretLineBackground.desired;
+ caretLineAlpha = source.caretLineAlpha;
+ edgecolour.desired = source.edgecolour.desired;
+ edgeState = source.edgeState;
+ caretWidth = source.caretWidth;
+ someStylesProtected = false;
+ leftMarginWidth = source.leftMarginWidth;
+ rightMarginWidth = source.rightMarginWidth;
+ for (int i=0;i < margins; i++) {
+ ms[i] = source.ms[i];
+ }
+ symbolMargin = source.symbolMargin;
+ maskInLine = source.maskInLine;
+ fixedColumnWidth = source.fixedColumnWidth;
+ zoomLevel = source.zoomLevel;
+ viewWhitespace = source.viewWhitespace;
+ viewIndentationGuides = source.viewIndentationGuides;
+ viewEOL = source.viewEOL;
+ showMarkedLines = source.showMarkedLines;
+ extraFontFlag = source.extraFontFlag;
+}
+
+ViewStyle::~ViewStyle() {
+}
+
+void ViewStyle::Init() {
+ fontNames.Clear();
+ ResetDefaultStyle();
+
+ indicators[0].style = INDIC_SQUIGGLE;
+ indicators[0].fore = ColourDesired(0, 0x7f, 0);
+ indicators[1].style = INDIC_TT;
+ indicators[1].fore = ColourDesired(0, 0, 0xff);
+ indicators[2].style = INDIC_PLAIN;
+ indicators[2].fore = ColourDesired(0xff, 0, 0);
+
+ lineHeight = 1;
+ maxAscent = 1;
+ maxDescent = 1;
+ aveCharWidth = 8;
+ spaceWidth = 8;
+
+ selforeset = false;
+ selforeground.desired = ColourDesired(0xff, 0, 0);
+ selbackset = true;
+ selbackground.desired = ColourDesired(0xc0, 0xc0, 0xc0);
+ selbackground2.desired = ColourDesired(0xb0, 0xb0, 0xb0);
+ selAlpha = SC_ALPHA_NOALPHA;
+
+ foldmarginColourSet = false;
+ foldmarginColour.desired = ColourDesired(0xff, 0, 0);
+ foldmarginHighlightColourSet = false;
+ foldmarginHighlightColour.desired = ColourDesired(0xc0, 0xc0, 0xc0);
+
+ whitespaceForegroundSet = false;
+ whitespaceForeground.desired = ColourDesired(0, 0, 0);
+ whitespaceBackgroundSet = false;
+ whitespaceBackground.desired = ColourDesired(0xff, 0xff, 0xff);
+ selbar.desired = Platform::Chrome();
+ selbarlight.desired = Platform::ChromeHighlight();
+ styles[STYLE_LINENUMBER].fore.desired = ColourDesired(0, 0, 0);
+ styles[STYLE_LINENUMBER].back.desired = Platform::Chrome();
+ caretcolour.desired = ColourDesired(0, 0, 0);
+ showCaretLineBackground = false;
+ caretLineBackground.desired = ColourDesired(0xff, 0xff, 0);
+ caretLineAlpha = SC_ALPHA_NOALPHA;
+ edgecolour.desired = ColourDesired(0xc0, 0xc0, 0xc0);
+ edgeState = EDGE_NONE;
+ caretWidth = 1;
+ someStylesProtected = false;
+
+ hotspotForegroundSet = false;
+ hotspotForeground.desired = ColourDesired(0, 0, 0xff);
+ hotspotBackgroundSet = false;
+ hotspotBackground.desired = ColourDesired(0xff, 0xff, 0xff);
+ hotspotUnderline = true;
+ hotspotSingleLine = true;
+
+ leftMarginWidth = 1;
+ rightMarginWidth = 1;
+ ms[0].style = SC_MARGIN_NUMBER;
+ ms[0].width = 0;
+ ms[0].mask = 0;
+ ms[1].style = SC_MARGIN_SYMBOL;
+ ms[1].width = 16;
+ ms[1].mask = ~SC_MASK_FOLDERS;
+ ms[2].style = SC_MARGIN_SYMBOL;
+ ms[2].width = 0;
+ ms[2].mask = 0;
+ fixedColumnWidth = leftMarginWidth;
+ symbolMargin = false;
+ maskInLine = 0xffffffff;
+ for (int margin=0; margin < margins; margin++) {
+ fixedColumnWidth += ms[margin].width;
+ symbolMargin = symbolMargin || (ms[margin].style != SC_MARGIN_NUMBER);
+ if (ms[margin].width > 0)
+ maskInLine &= ~ms[margin].mask;
+ }
+ zoomLevel = 0;
+ viewWhitespace = wsInvisible;
+ viewIndentationGuides = false;
+ viewEOL = false;
+ showMarkedLines = true;
+ extraFontFlag = false;
+}
+
+void ViewStyle::RefreshColourPalette(Palette &pal, bool want) {
+ unsigned int i;
+ for (i=0;i<(sizeof(styles)/sizeof(styles[0]));i++) {
+ pal.WantFind(styles[i].fore, want);
+ pal.WantFind(styles[i].back, want);
+ }
+ for (i=0;i<(sizeof(indicators)/sizeof(indicators[0]));i++) {
+ pal.WantFind(indicators[i].fore, want);
+ }
+ for (i=0;i<(sizeof(markers)/sizeof(markers[0]));i++) {
+ markers[i].RefreshColourPalette(pal, want);
+ }
+ pal.WantFind(selforeground, want);
+ pal.WantFind(selbackground, want);
+ pal.WantFind(selbackground2, want);
+
+ pal.WantFind(foldmarginColour, want);
+ pal.WantFind(foldmarginHighlightColour, want);
+
+ pal.WantFind(whitespaceForeground, want);
+ pal.WantFind(whitespaceBackground, want);
+ pal.WantFind(selbar, want);
+ pal.WantFind(selbarlight, want);
+ pal.WantFind(caretcolour, want);
+ pal.WantFind(caretLineBackground, want);
+ pal.WantFind(edgecolour, want);
+ pal.WantFind(hotspotForeground, want);
+ pal.WantFind(hotspotBackground, want);
+}
+
+void ViewStyle::Refresh(Surface &surface) {
+ selbar.desired = Platform::Chrome();
+ selbarlight.desired = Platform::ChromeHighlight();
+ styles[STYLE_DEFAULT].Realise(surface, zoomLevel, NULL, extraFontFlag);
+ maxAscent = styles[STYLE_DEFAULT].ascent;
+ maxDescent = styles[STYLE_DEFAULT].descent;
+ someStylesProtected = false;
+ for (unsigned int i=0;i<(sizeof(styles)/sizeof(styles[0]));i++) {
+ if (i != STYLE_DEFAULT) {
+ styles[i].Realise(surface, zoomLevel, &styles[STYLE_DEFAULT], extraFontFlag);
+ if (maxAscent < styles[i].ascent)
+ maxAscent = styles[i].ascent;
+ if (maxDescent < styles[i].descent)
+ maxDescent = styles[i].descent;
+ }
+ if (styles[i].IsProtected()) {
+ someStylesProtected = true;
+ }
+ }
+
+ lineHeight = maxAscent + maxDescent;
+ aveCharWidth = styles[STYLE_DEFAULT].aveCharWidth;
+ spaceWidth = styles[STYLE_DEFAULT].spaceWidth;
+
+ fixedColumnWidth = leftMarginWidth;
+ symbolMargin = false;
+ maskInLine = 0xffffffff;
+ for (int margin=0; margin < margins; margin++) {
+ fixedColumnWidth += ms[margin].width;
+ symbolMargin = symbolMargin || (ms[margin].style != SC_MARGIN_NUMBER);
+ if (ms[margin].width > 0)
+ maskInLine &= ~ms[margin].mask;
+ }
+}
+
+void ViewStyle::ResetDefaultStyle() {
+ styles[STYLE_DEFAULT].Clear(ColourDesired(0,0,0),
+ ColourDesired(0xff,0xff,0xff),
+ Platform::DefaultFontSize(), fontNames.Save(Platform::DefaultFont()),
+ SC_CHARSET_DEFAULT,
+ false, false, false, false, Style::caseMixed, true, true, false);
+}
+
+void ViewStyle::ClearStyles() {
+ // Reset all styles to be like the default style
+ for (unsigned int i=0;i<(sizeof(styles)/sizeof(styles[0]));i++) {
+ if (i != STYLE_DEFAULT) {
+ styles[i].ClearTo(styles[STYLE_DEFAULT]);
+ }
+ }
+ styles[STYLE_LINENUMBER].back.desired = Platform::Chrome();
+
+ // Set call tip fore/back to match the values previously set for call tips
+ styles[STYLE_CALLTIP].back.desired = ColourDesired(0xff, 0xff, 0xff);
+ styles[STYLE_CALLTIP].fore.desired = ColourDesired(0x80, 0x80, 0x80);
+}
+
+void ViewStyle::SetStyleFontName(int styleIndex, const char *name) {
+ styles[styleIndex].fontName = fontNames.Save(name);
+}
+
+bool ViewStyle::ProtectionActive() const {
+ return someStylesProtected;
+}
diff --git a/src/ViewStyle.h b/src/ViewStyle.h
new file mode 100755
index 0000000..75f899d
--- /dev/null
+++ b/src/ViewStyle.h
@@ -0,0 +1,108 @@
+// Scintilla source code edit control
+/** @file ViewStyle.h
+ ** Store information on how the document is to be viewed.
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef VIEWSTYLE_H
+#define VIEWSTYLE_H
+
+/**
+ */
+class MarginStyle {
+public:
+ int style;
+ int width;
+ int mask;
+ bool sensitive;
+ MarginStyle();
+};
+
+/**
+ */
+class FontNames {
+private:
+ char *names[STYLE_MAX + 1];
+ int max;
+
+public:
+ FontNames();
+ ~FontNames();
+ void Clear();
+ const char *Save(const char *name);
+};
+
+enum WhiteSpaceVisibility {wsInvisible=0, wsVisibleAlways=1, wsVisibleAfterIndent=2};
+
+/**
+ */
+class ViewStyle {
+public:
+ FontNames fontNames;
+ Style styles[STYLE_MAX + 1];
+ LineMarker markers[MARKER_MAX + 1];
+ Indicator indicators[INDIC_MAX + 1];
+ int lineHeight;
+ unsigned int maxAscent;
+ unsigned int maxDescent;
+ unsigned int aveCharWidth;
+ unsigned int spaceWidth;
+ bool selforeset;
+ ColourPair selforeground;
+ bool selbackset;
+ ColourPair selbackground;
+ ColourPair selbackground2;
+ int selAlpha;
+ bool whitespaceForegroundSet;
+ ColourPair whitespaceForeground;
+ bool whitespaceBackgroundSet;
+ ColourPair whitespaceBackground;
+ ColourPair selbar;
+ ColourPair selbarlight;
+ bool foldmarginColourSet;
+ ColourPair foldmarginColour;
+ bool foldmarginHighlightColourSet;
+ ColourPair foldmarginHighlightColour;
+ bool hotspotForegroundSet;
+ ColourPair hotspotForeground;
+ bool hotspotBackgroundSet;
+ ColourPair hotspotBackground;
+ bool hotspotUnderline;
+ bool hotspotSingleLine;
+ /// Margins are ordered: Line Numbers, Selection Margin, Spacing Margin
+ enum { margins=5 };
+ int leftMarginWidth; ///< Spacing margin on left of text
+ int rightMarginWidth; ///< Spacing margin on left of text
+ bool symbolMargin;
+ int maskInLine; ///< Mask for markers to be put into text because there is nowhere for them to go in margin
+ MarginStyle ms[margins];
+ int fixedColumnWidth;
+ int zoomLevel;
+ WhiteSpaceVisibility viewWhitespace;
+ bool viewIndentationGuides;
+ bool viewEOL;
+ bool showMarkedLines;
+ ColourPair caretcolour;
+ bool showCaretLineBackground;
+ ColourPair caretLineBackground;
+ int caretLineAlpha;
+ ColourPair edgecolour;
+ int edgeState;
+ int caretWidth;
+ bool someStylesProtected;
+ bool extraFontFlag;
+
+ ViewStyle();
+ ViewStyle(const ViewStyle &source);
+ ~ViewStyle();
+ void Init();
+ void RefreshColourPalette(Palette &pal, bool want);
+ void Refresh(Surface &surface);
+ void ResetDefaultStyle();
+ void ClearStyles();
+ void SetStyleFontName(int styleIndex, const char *name);
+ bool ProtectionActive() const;
+};
+
+#endif
diff --git a/src/WindowAccessor.cpp b/src/WindowAccessor.cpp
new file mode 100755
index 0000000..ce42534
--- /dev/null
+++ b/src/WindowAccessor.cpp
@@ -0,0 +1,178 @@
+// Scintilla source code edit control
+/** @file WindowAccessor.cxx
+ ** Rapid easy access to contents of a Scintilla.
+ **/
+// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+
+#include "Platform.h"
+
+#include "PropSet.h"
+#include "Accessor.h"
+#include "WindowAccessor.h"
+#include "Scintilla.h"
+
+WindowAccessor::~WindowAccessor() {
+}
+
+bool WindowAccessor::InternalIsLeadByte(char ch) {
+ if (SC_CP_UTF8 == codePage)
+ // For lexing, all characters >= 0x80 are treated the
+ // same so none is considered a lead byte.
+ return false;
+ else
+ return Platform::IsDBCSLeadByte(codePage, ch);
+}
+
+void WindowAccessor::Fill(int position) {
+ if (lenDoc == -1)
+ lenDoc = Platform::SendScintilla(id, SCI_GETTEXTLENGTH, 0, 0);
+ startPos = position - slopSize;
+ if (startPos + bufferSize > lenDoc)
+ startPos = lenDoc - bufferSize;
+ if (startPos < 0)
+ startPos = 0;
+ endPos = startPos + bufferSize;
+ if (endPos > lenDoc)
+ endPos = lenDoc;
+
+ TextRange tr = {{startPos, endPos}, buf};
+ Platform::SendScintillaPointer(id, SCI_GETTEXTRANGE, 0, &tr);
+}
+
+bool WindowAccessor::Match(int pos, const char *s) {
+ for (int i=0; *s; i++) {
+ if (*s != SafeGetCharAt(pos+i))
+ return false;
+ s++;
+ }
+ return true;
+}
+
+char WindowAccessor::StyleAt(int position) {
+ return static_cast<char>(Platform::SendScintilla(
+ id, SCI_GETSTYLEAT, position, 0));
+}
+
+int WindowAccessor::GetLine(int position) {
+ return Platform::SendScintilla(id, SCI_LINEFROMPOSITION, position, 0);
+}
+
+int WindowAccessor::LineStart(int line) {
+ return Platform::SendScintilla(id, SCI_POSITIONFROMLINE, line, 0);
+}
+
+int WindowAccessor::LevelAt(int line) {
+ return Platform::SendScintilla(id, SCI_GETFOLDLEVEL, line, 0);
+}
+
+int WindowAccessor::Length() {
+ if (lenDoc == -1)
+ lenDoc = Platform::SendScintilla(id, SCI_GETTEXTLENGTH, 0, 0);
+ return lenDoc;
+}
+
+int WindowAccessor::GetLineState(int line) {
+ return Platform::SendScintilla(id, SCI_GETLINESTATE, line);
+}
+
+int WindowAccessor::SetLineState(int line, int state) {
+ return Platform::SendScintilla(id, SCI_SETLINESTATE, line, state);
+}
+
+void WindowAccessor::StartAt(unsigned int start, char chMask) {
+ Platform::SendScintilla(id, SCI_STARTSTYLING, start, chMask);
+}
+
+void WindowAccessor::StartSegment(unsigned int pos) {
+ startSeg = pos;
+}
+
+void WindowAccessor::ColourTo(unsigned int pos, int chAttr) {
+ // Only perform styling if non empty range
+ if (pos != startSeg - 1) {
+ if (pos < startSeg) {
+ Platform::DebugPrintf("Bad colour positions %d - %d\n", startSeg, pos);
+ }
+
+ if (validLen + (pos - startSeg + 1) >= bufferSize)
+ Flush();
+ if (validLen + (pos - startSeg + 1) >= bufferSize) {
+ // Too big for buffer so send directly
+ Platform::SendScintilla(id, SCI_SETSTYLING, pos - startSeg + 1, chAttr);
+ } else {
+ if (chAttr != chWhile)
+ chFlags = 0;
+ chAttr |= chFlags;
+ for (unsigned int i = startSeg; i <= pos; i++) {
+ styleBuf[validLen++] = static_cast<char>(chAttr);
+ }
+ }
+ }
+ startSeg = pos+1;
+}
+
+void WindowAccessor::SetLevel(int line, int level) {
+ Platform::SendScintilla(id, SCI_SETFOLDLEVEL, line, level);
+}
+
+void WindowAccessor::Flush() {
+ startPos = extremePosition;
+ lenDoc = -1;
+ if (validLen > 0) {
+ Platform::SendScintillaPointer(id, SCI_SETSTYLINGEX, validLen,
+ styleBuf);
+ validLen = 0;
+ }
+}
+
+int WindowAccessor::IndentAmount(int line, int *flags, PFNIsCommentLeader pfnIsCommentLeader) {
+ int 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.
+
+ int pos = LineStart(line);
+ char ch = (*this)[pos];
+ int indent = 0;
+ bool inPrevPrefix = line > 0;
+ int posPrev = inPrevPrefix ? LineStart(line-1) : 0;
+ while ((ch == ' ' || ch == '\t') && (pos < end)) {
+ if (inPrevPrefix) {
+ 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 (isspace(ch) || (pfnIsCommentLeader && (*pfnIsCommentLeader)(*this, pos, end-pos)) )
+ return indent | SC_FOLDLEVELWHITEFLAG;
+ else
+ return indent;
+}
+
diff --git a/src/XPM.cpp b/src/XPM.cpp
new file mode 100644
index 0000000..b8e27f4
--- /dev/null
+++ b/src/XPM.cpp
@@ -0,0 +1,348 @@
+// Scintilla source code edit control
+/** @file XPM.cxx
+ ** Define a class that holds data in the X Pixmap (XPM) format.
+ **/
+// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "Platform.h"
+
+#include "XPM.h"
+
+#if defined(PLAT_QT)
+
+XPM::XPM(const char *textForm)
+{
+ qpm = *reinterpret_cast<const QPixmap *>(textForm);
+}
+
+XPM::XPM(const char * const *linesForm)
+{
+ qpm = *reinterpret_cast<const QPixmap *>(linesForm);
+}
+
+void XPM::RefreshColourPalette(Palette &pal, bool want)
+{
+ // Nothing to do.
+}
+
+void XPM::Draw(Surface *surface, PRectangle &rc)
+{
+ surface -> DrawXPM(rc,this);
+}
+
+#else
+
+static const char *NextField(const char *s) {
+ // In case there are leading spaces in the string
+ while (*s && *s == ' ') {
+ s++;
+ }
+ while (*s && *s != ' ') {
+ s++;
+ }
+ while (*s && *s == ' ') {
+ s++;
+ }
+ return s;
+}
+
+// Data lines in XPM can be terminated either with NUL or "
+static size_t MeasureLength(const char *s) {
+ size_t i = 0;
+ while (s[i] && (s[i] != '\"'))
+ i++;
+ return i;
+}
+
+ColourAllocated XPM::ColourFromCode(int ch) {
+ return colourCodeTable[ch]->allocated;
+#ifdef SLOW
+ for (int i=0; i<nColours; i++) {
+ if (codes[i] == ch) {
+ return colours[i].allocated;
+ }
+ }
+ return colours[0].allocated;
+#endif
+}
+
+void XPM::FillRun(Surface *surface, int code, int startX, int y, int x) {
+ if ((code != codeTransparent) && (startX != x)) {
+ PRectangle rc(startX, y, x, y+1);
+ surface->FillRectangle(rc, ColourFromCode(code));
+ }
+}
+
+XPM::XPM(const char *textForm) :
+ data(0), codes(0), colours(0), lines(0) {
+ Init(textForm);
+}
+
+XPM::XPM(const char * const *linesForm) :
+ data(0), codes(0), colours(0), lines(0) {
+ Init(linesForm);
+}
+
+XPM::~XPM() {
+ Clear();
+}
+
+void XPM::Init(const char *textForm) {
+ Clear();
+ // Test done is two parts to avoid possibility of overstepping the memory
+ // if memcmp implemented strangely. Must be 4 bytes at least at destination.
+ if ((0 == memcmp(textForm, "/* X", 4)) && (0 == memcmp(textForm, "/* XPM */", 9))) {
+ // Build the lines form out of the text form
+ const char **linesForm = LinesFormFromTextForm(textForm);
+ if (linesForm != 0) {
+ Init(linesForm);
+ delete []linesForm;
+ }
+ } else {
+ // It is really in line form
+ Init(reinterpret_cast<const char * const *>(textForm));
+ }
+}
+
+void XPM::Init(const char * const *linesForm) {
+ Clear();
+ height = 1;
+ width = 1;
+ nColours = 1;
+ data = NULL;
+ codeTransparent = ' ';
+ codes = NULL;
+ colours = NULL;
+ lines = NULL;
+ if (!linesForm)
+ return;
+
+ const char *line0 = linesForm[0];
+ width = atoi(line0);
+ line0 = NextField(line0);
+ height = atoi(line0);
+ line0 = NextField(line0);
+ nColours = atoi(line0);
+ line0 = NextField(line0);
+ if (atoi(line0) != 1) {
+ // Only one char per pixel is supported
+ return;
+ }
+ codes = new char[nColours];
+ colours = new ColourPair[nColours];
+
+ int strings = 1+height+nColours;
+ lines = new char *[strings];
+ size_t allocation = 0;
+ for (int i=0; i<strings; i++) {
+ allocation += MeasureLength(linesForm[i]) + 1;
+ }
+ data = new char[allocation];
+ char *nextBit = data;
+ for (int j=0; j<strings; j++) {
+ lines[j] = nextBit;
+ size_t len = MeasureLength(linesForm[j]);
+ memcpy(nextBit, linesForm[j], len);
+ nextBit += len;
+ *nextBit++ = '\0';
+ }
+
+ for (int code=0; code<256; code++) {
+ colourCodeTable[code] = 0;
+ }
+
+ for (int c=0; c<nColours; c++) {
+ const char *colourDef = linesForm[c+1];
+ codes[c] = colourDef[0];
+ colourDef += 4;
+ if (*colourDef == '#') {
+ colours[c].desired.Set(colourDef);
+ } else {
+ colours[c].desired = ColourDesired(0xff, 0xff, 0xff);
+ codeTransparent = codes[c];
+ }
+ colourCodeTable[static_cast<unsigned char>(codes[c])] = &(colours[c]);
+ }
+}
+
+void XPM::Clear() {
+ delete []data;
+ data = 0;
+ delete []codes;
+ codes = 0;
+ delete []colours;
+ colours = 0;
+ delete []lines;
+ lines = 0;
+}
+
+void XPM::RefreshColourPalette(Palette &pal, bool want) {
+ if (!data || !codes || !colours || !lines) {
+ return;
+ }
+ for (int i=0; i<nColours; i++) {
+ pal.WantFind(colours[i], want);
+ }
+}
+
+void XPM::CopyDesiredColours() {
+ if (!data || !codes || !colours || !lines) {
+ return;
+ }
+ for (int i=0; i<nColours; i++) {
+ colours[i].Copy();
+ }
+}
+
+void XPM::Draw(Surface *surface, PRectangle &rc) {
+ if (!data || !codes || !colours || !lines) {
+ return;
+ }
+ // Centre the pixmap
+ int startY = rc.top + (rc.Height() - height) / 2;
+ int startX = rc.left + (rc.Width() - width) / 2;
+ for (int y=0;y<height;y++) {
+ int prevCode = 0;
+ int xStartRun = 0;
+ for (int x=0; x<width; x++) {
+ int code = lines[y+nColours+1][x];
+ if (code != prevCode) {
+ FillRun(surface, prevCode, startX + xStartRun, startY + y, startX + x);
+ xStartRun = x;
+ prevCode = code;
+ }
+ }
+ FillRun(surface, prevCode, startX + xStartRun, startY + y, startX + width);
+ }
+}
+
+const char **XPM::LinesFormFromTextForm(const char *textForm) {
+ // Build the lines form out of the text form
+ const char **linesForm = 0;
+ int countQuotes = 0;
+ int strings=1;
+ int j=0;
+ for (; countQuotes < (2*strings) && textForm[j] != '\0'; j++) {
+ if (textForm[j] == '\"') {
+ if (countQuotes == 0) {
+ // First field: width, height, number of colors, chars per pixel
+ const char *line0 = textForm + j + 1;
+ // Skip width
+ line0 = NextField(line0);
+ // Add 1 line for each pixel of height
+ strings += atoi(line0);
+ line0 = NextField(line0);
+ // Add 1 line for each colour
+ strings += atoi(line0);
+ linesForm = new const char *[strings];
+ if (linesForm == 0) {
+ break; // Memory error!
+ }
+ }
+ if (countQuotes / 2 >= strings) {
+ break; // Bad height or number of colors!
+ }
+ if ((countQuotes & 1) == 0) {
+ linesForm[countQuotes / 2] = textForm + j + 1;
+ }
+ countQuotes++;
+ }
+ }
+ if (textForm[j] == '\0' || countQuotes / 2 > strings) {
+ // Malformed XPM! Height + number of colors too high or too low
+ delete []linesForm;
+ linesForm = 0;
+ }
+ return linesForm;
+}
+
+// In future, may want to minimize search time by sorting and using a binary search.
+
+XPMSet::XPMSet() : set(0), len(0), maximum(0), height(-1), width(-1) {
+}
+
+XPMSet::~XPMSet() {
+ Clear();
+}
+
+void XPMSet::Clear() {
+ for (int i = 0; i < len; i++) {
+ delete set[i];
+ }
+ delete []set;
+ set = 0;
+ len = 0;
+ maximum = 0;
+ height = -1;
+ width = -1;
+}
+
+void XPMSet::Add(int id, const char *textForm) {
+ // Invalidate cached dimensions
+ height = -1;
+ width = -1;
+
+ // Replace if this id already present
+ for (int i = 0; i < len; i++) {
+ if (set[i]->GetId() == id) {
+ set[i]->Init(textForm);
+ set[i]->CopyDesiredColours();
+ return;
+ }
+ }
+
+ // Not present, so add to end
+ XPM *pxpm = new XPM(textForm);
+ if (pxpm) {
+ pxpm->SetId(id);
+ pxpm->CopyDesiredColours();
+ if (len == maximum) {
+ maximum += 64;
+ XPM **setNew = new XPM *[maximum];
+ for (int i = 0; i < len; i++) {
+ setNew[i] = set[i];
+ }
+ delete []set;
+ set = setNew;
+ }
+ set[len] = pxpm;
+ len++;
+ }
+}
+
+XPM *XPMSet::Get(int id) {
+ for (int i = 0; i < len; i++) {
+ if (set[i]->GetId() == id) {
+ return set[i];
+ }
+ }
+ return 0;
+}
+
+int XPMSet::GetHeight() {
+ if (height < 0) {
+ for (int i = 0; i < len; i++) {
+ if (height < set[i]->GetHeight()) {
+ height = set[i]->GetHeight();
+ }
+ }
+ }
+ return (height > 0) ? height : 0;
+}
+
+int XPMSet::GetWidth() {
+ if (width < 0) {
+ for (int i = 0; i < len; i++) {
+ if (width < set[i]->GetWidth()) {
+ width = set[i]->GetWidth();
+ }
+ }
+ }
+ return (width > 0) ? width : 0;
+}
+
+#endif
diff --git a/src/XPM.h b/src/XPM.h
new file mode 100644
index 0000000..962ffe4
--- /dev/null
+++ b/src/XPM.h
@@ -0,0 +1,94 @@
+// Scintilla source code edit control
+/** @file XPM.h
+ ** Define a class that holds data in the X Pixmap (XPM) format.
+ **/
+// Copyright 1998-2003 by Neil Hodgson <neilh@scintilla.org>
+// The License.txt file describes the conditions under which this software may be distributed.
+
+#ifndef XPM_H
+#define XPM_H
+
+#if defined(PLAT_QT)
+#include <qpixmap.h>
+#endif
+
+/**
+ * Hold a pixmap in XPM format.
+ */
+class XPM {
+#if defined(PLAT_QT)
+ QPixmap qpm;
+
+public:
+ XPM(const char *textForm);
+ XPM(const char * const *linesForm);
+ ~XPM() {}
+
+ void RefreshColourPalette(Palette &pal, bool want);
+ void Draw(Surface *surface,PRectangle &rc);
+
+ const QPixmap &Pixmap() const {return qpm;}
+#else
+ int id; // Assigned by container
+ int height;
+ int width;
+ int nColours;
+ char *data;
+ char codeTransparent;
+ char *codes;
+ ColourPair *colours;
+ ColourAllocated ColourFromCode(int ch);
+ void FillRun(Surface *surface, int code, int startX, int y, int x);
+ char **lines;
+ ColourPair *colourCodeTable[256];
+public:
+ XPM(const char *textForm);
+ XPM(const char * const *linesForm);
+ ~XPM();
+ void Init(const char *textForm);
+ void Init(const char * const *linesForm);
+ void Clear();
+ /// Similar to same named method in ViewStyle:
+ void RefreshColourPalette(Palette &pal, bool want);
+ /// No palette used, so just copy the desired colours to the allocated colours
+ void CopyDesiredColours();
+ /// Decompose image into runs and use FillRectangle for each run
+ void Draw(Surface *surface, PRectangle &rc);
+ char **InLinesForm() { return lines; }
+ void SetId(int id_) { id = id_; }
+ int GetId() { return id; }
+ int GetHeight() { return height; }
+ int GetWidth() { return width; }
+ static const char **LinesFormFromTextForm(const char *textForm);
+#endif
+};
+
+#if !defined(PLAT_QT)
+
+/**
+ * A collection of pixmaps indexed by integer id.
+ */
+class XPMSet {
+ XPM **set; ///< The stored XPMs.
+ int len; ///< Current number of XPMs.
+ int maximum; ///< Current maximum number of XPMs, increased by steps if reached.
+ int height; ///< Memorize largest height of the set.
+ int width; ///< Memorize largest width of the set.
+public:
+ XPMSet();
+ ~XPMSet();
+ /// Remove all XPMs.
+ void Clear();
+ /// Add a XPM.
+ void Add(int id, const char *textForm);
+ /// Get XPM by id.
+ XPM *Get(int id);
+ /// Give the largest height of the set.
+ int GetHeight();
+ /// Give the largest width of the set.
+ int GetWidth();
+};
+
+#endif
+
+#endif