Initial commit: ReclassX structured binary editor

This commit is contained in:
sysadmin
2026-02-01 11:37:32 -07:00
commit 0be67c8396
786 changed files with 473499 additions and 0 deletions

View File

@@ -0,0 +1,293 @@
// 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 <cstddef>
#include <cstdlib>
#include <cassert>
#include <cstring>
#include <cstdio>
#include <stdexcept>
#include <string>
#include <vector>
#include <algorithm>
#include <memory>
#include "Platform.h"
#include "Scintilla.h"
#include "CharacterSet.h"
#include "Position.h"
#include "AutoComplete.h"
using namespace Scintilla;
AutoComplete::AutoComplete() :
active(false),
separator(' '),
typesep('?'),
ignoreCase(false),
chooseSingle(false),
posStart(0),
startLen(0),
cancelAtStartPos(true),
autoHide(true),
dropRestOfWord(false),
ignoreCaseBehaviour(SC_CASEINSENSITIVEBEHAVIOUR_RESPECTCASE),
widthLBDefault(100),
heightLBDefault(100),
autoSort(SC_ORDER_PRESORTED) {
lb.reset(ListBox::Allocate());
}
AutoComplete::~AutoComplete() {
if (lb) {
lb->Destroy();
}
}
bool AutoComplete::Active() const noexcept {
return active;
}
void AutoComplete::Start(Window &parent, int ctrlID,
Sci::Position position, Point location, Sci::Position startLen_,
int lineHeight, bool unicodeMode, int technology) {
if (active) {
Cancel();
}
lb->Create(parent, ctrlID, location, lineHeight, unicodeMode, technology);
lb->Clear();
active = true;
startLen = startLen_;
posStart = position;
}
void AutoComplete::SetStopChars(const char *stopChars_) {
stopChars = stopChars_;
}
bool AutoComplete::IsStopChar(char ch) const noexcept {
return ch && (stopChars.find(ch) != std::string::npos);
}
void AutoComplete::SetFillUpChars(const char *fillUpChars_) {
fillUpChars = fillUpChars_;
}
bool AutoComplete::IsFillUpChar(char ch) const noexcept {
return ch && (fillUpChars.find(ch) != std::string::npos);
}
void AutoComplete::SetSeparator(char separator_) {
separator = separator_;
}
char AutoComplete::GetSeparator() const noexcept {
return separator;
}
void AutoComplete::SetTypesep(char separator_) {
typesep = separator_;
}
char AutoComplete::GetTypesep() const noexcept {
return typesep;
}
struct Sorter {
AutoComplete *ac;
const char *list;
std::vector<int> indices;
Sorter(AutoComplete *ac_, const char *list_) : ac(ac_), list(list_) {
int i = 0;
while (list[i]) {
indices.push_back(i); // word start
while (list[i] != ac->GetTypesep() && list[i] != ac->GetSeparator() && list[i])
++i;
indices.push_back(i); // word end
if (list[i] == ac->GetTypesep()) {
while (list[i] != ac->GetSeparator() && list[i])
++i;
}
if (list[i] == ac->GetSeparator()) {
++i;
// preserve trailing separator as blank entry
if (!list[i]) {
indices.push_back(i);
indices.push_back(i);
}
}
}
indices.push_back(i); // index of last position
}
bool operator()(int a, int b) {
const int lenA = indices[a * 2 + 1] - indices[a * 2];
const int lenB = indices[b * 2 + 1] - indices[b * 2];
const int len = std::min(lenA, lenB);
int cmp;
if (ac->ignoreCase)
cmp = CompareNCaseInsensitive(list + indices[a * 2], list + indices[b * 2], len);
else
cmp = strncmp(list + indices[a * 2], list + indices[b * 2], len);
if (cmp == 0)
cmp = lenA - lenB;
return cmp < 0;
}
};
void AutoComplete::SetList(const char *list) {
if (autoSort == SC_ORDER_PRESORTED) {
lb->SetList(list, separator, typesep);
sortMatrix.clear();
for (int i = 0; i < lb->Length(); ++i)
sortMatrix.push_back(i);
return;
}
Sorter IndexSort(this, list);
sortMatrix.clear();
for (int i = 0; i < static_cast<int>(IndexSort.indices.size()) / 2; ++i)
sortMatrix.push_back(i);
std::sort(sortMatrix.begin(), sortMatrix.end(), IndexSort);
if (autoSort == SC_ORDER_CUSTOM || sortMatrix.size() < 2) {
lb->SetList(list, separator, typesep);
PLATFORM_ASSERT(lb->Length() == static_cast<int>(sortMatrix.size()));
return;
}
std::string sortedList;
char item[maxItemLen];
for (size_t i = 0; i < sortMatrix.size(); ++i) {
int wordLen = IndexSort.indices[sortMatrix[i] * 2 + 2] - IndexSort.indices[sortMatrix[i] * 2];
if (wordLen > maxItemLen-2)
wordLen = maxItemLen - 2;
memcpy(item, list + IndexSort.indices[sortMatrix[i] * 2], wordLen);
if ((i+1) == sortMatrix.size()) {
// Last item so remove separator if present
if ((wordLen > 0) && (item[wordLen-1] == separator))
wordLen--;
} else {
// Item before last needs a separator
if ((wordLen == 0) || (item[wordLen-1] != separator)) {
item[wordLen] = separator;
wordLen++;
}
}
item[wordLen] = '\0';
sortedList += item;
}
for (int i = 0; i < static_cast<int>(sortMatrix.size()); ++i)
sortMatrix[i] = i;
lb->SetList(sortedList.c_str(), separator, typesep);
}
int AutoComplete::GetSelection() const {
return lb->GetSelection();
}
std::string AutoComplete::GetValue(int item) const {
char value[maxItemLen];
lb->GetValue(item, value, sizeof(value));
return std::string(value);
}
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) {
const 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) {
const size_t lenWord = strlen(word);
int location = -1;
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;
char item[maxItemLen];
lb->GetValue(sortMatrix[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(sortMatrix[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
&& ignoreCaseBehaviour == SC_CASEINSENSITIVEBEHAVIOUR_RESPECTCASE) {
// Check for exact-case match
for (; pivot <= end; pivot++) {
lb->GetValue(sortMatrix[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) {
if (autoHide)
Cancel();
else
lb->Select(-1);
} else {
if (autoSort == SC_ORDER_CUSTOM) {
// Check for a logically earlier match
char item[maxItemLen];
for (int i = location + 1; i <= end; ++i) {
lb->GetValue(sortMatrix[i], item, maxItemLen);
if (CompareNCaseInsensitive(word, item, lenWord))
break;
if (sortMatrix[i] < sortMatrix[location] && !strncmp(word, item, lenWord))
location = i;
}
}
lb->Select(sortMatrix[location]);
}
}

View File

@@ -0,0 +1,91 @@
// 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
namespace Scintilla {
/**
*/
class AutoComplete {
bool active;
std::string stopChars;
std::string fillUpChars;
char separator;
char typesep; // Type seperator
enum { maxItemLen=1000 };
std::vector<int> sortMatrix;
public:
bool ignoreCase;
bool chooseSingle;
std::unique_ptr<ListBox> lb;
Sci::Position posStart;
Sci::Position startLen;
/// Should autocompletion be canceled if editor's currentPos <= startPos?
bool cancelAtStartPos;
bool autoHide;
bool dropRestOfWord;
unsigned int ignoreCaseBehaviour;
int widthLBDefault;
int heightLBDefault;
/** SC_ORDER_PRESORTED: Assume the list is presorted; selection will fail if it is not alphabetical<br />
* SC_ORDER_PERFORMSORT: Sort the list alphabetically; start up performance cost for sorting<br />
* SC_ORDER_CUSTOM: Handle non-alphabetical entries; start up performance cost for generating a sorted lookup table
*/
int autoSort;
AutoComplete();
~AutoComplete();
/// Is the auto completion list displayed?
bool Active() const noexcept;
/// Display the auto completion list positioned to be near a character position
void Start(Window &parent, int ctrlID, Sci::Position position, Point location,
Sci::Position startLen_, int lineHeight, bool unicodeMode, int technology);
/// The stop chars are characters which, when typed, cause the auto completion list to disappear
void SetStopChars(const char *stopChars_);
bool IsStopChar(char ch) const noexcept;
/// The fillup chars are characters which, when typed, fill up the selected word
void SetFillUpChars(const char *fillUpChars_);
bool IsFillUpChar(char ch) const noexcept;
/// The separator character is used when interpreting the list in SetList
void SetSeparator(char separator_);
char GetSeparator() const noexcept;
/// The typesep character is used for separating the word from the type
void SetTypesep(char separator_);
char GetTypesep() const noexcept;
/// The list string contains a sequence of words separated by the separator character
void SetList(const char *list);
/// Return the position of the currently selected list item
int GetSelection() const;
/// Return the value of an item in the list
std::string GetValue(int item) const;
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

View File

@@ -0,0 +1,332 @@
// 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 <cstddef>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <stdexcept>
#include <string>
#include <vector>
#include <algorithm>
#include <memory>
#include "Platform.h"
#include "Scintilla.h"
#include "StringCopy.h"
#include "Position.h"
#include "IntegerRectangle.h"
#include "CallTip.h"
using namespace Scintilla;
CallTip::CallTip() {
wCallTip = 0;
inCallTipMode = false;
posStartCallTip = 0;
rectUp = PRectangle(0,0,0,0);
rectDown = PRectangle(0,0,0,0);
lineHeight = 1;
offsetMain = 0;
startHighlight = 0;
endHighlight = 0;
tabSize = 0;
above = false;
useStyleCallTip = false; // for backwards compatibility
insetX = 5;
widthArrow = 14;
borderHeight = 2; // Extra line for border and an empty line at top and bottom.
verticalOffset = 1;
#ifdef __APPLE__
// proper apple colours for the default
colourBG = ColourDesired(0xff, 0xff, 0xc6);
colourUnSel = ColourDesired(0, 0, 0);
#else
colourBG = ColourDesired(0xff, 0xff, 0xff);
colourUnSel = ColourDesired(0x80, 0x80, 0x80);
#endif
colourSel = ColourDesired(0, 0, 0x80);
colourShade = ColourDesired(0, 0, 0);
colourLight = ColourDesired(0xc0, 0xc0, 0xc0);
codePage = 0;
clickPlace = 0;
}
CallTip::~CallTip() {
font.Release();
wCallTip.Destroy();
}
// 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) const {
return (tabSize > 0) && (ch == '\t');
}
int CallTip::NextTabPos(int x) const {
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;
const 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++) {
const int endSeg = ends[seg];
if (endSeg > startSeg) {
if (IsArrowCharacter(s[startSeg])) {
xEnd = x + widthArrow;
const bool upArrow = s[startSeg] == '\001';
rcClient.left = static_cast<XYPOSITION>(x);
rcClient.right = static_cast<XYPOSITION>(xEnd);
if (draw) {
const int halfWidth = widthArrow / 2 - 3;
const int quarterWidth = halfWidth / 2;
const int centreX = x + widthArrow / 2 - 1;
const int centreY = static_cast<int>(rcClient.top + rcClient.bottom) / 2;
surface->FillRectangle(rcClient, colourBG);
const PRectangle rcClientInner(rcClient.left + 1, rcClient.top + 1,
rcClient.right - 2, rcClient.bottom - 1);
surface->FillRectangle(rcClientInner, colourUnSel);
if (upArrow) { // Up arrow
Point pts[] = {
Point::FromInts(centreX - halfWidth, centreY + quarterWidth),
Point::FromInts(centreX + halfWidth, centreY + quarterWidth),
Point::FromInts(centreX, centreY - halfWidth + quarterWidth),
};
surface->Polygon(pts, ELEMENTS(pts), colourBG, colourBG);
} else { // Down arrow
Point pts[] = {
Point::FromInts(centreX - halfWidth, centreY - quarterWidth),
Point::FromInts(centreX + halfWidth, centreY - quarterWidth),
Point::FromInts(centreX, centreY + halfWidth - quarterWidth),
};
surface->Polygon(pts, ELEMENTS(pts), colourBG, colourBG);
}
}
offsetMain = xEnd;
if (upArrow) {
rectUp = rcClient;
} else {
rectDown = rcClient;
}
} else if (IsTabCharacter(s[startSeg])) {
xEnd = NextTabPos(x);
} else {
xEnd = x + static_cast<int>(lround(surface->WidthText(font, s + startSeg, endSeg - startSeg)));
if (draw) {
rcClient.left = static_cast<XYPOSITION>(x);
rcClient.right = static_cast<XYPOSITION>(xEnd);
surface->DrawTextTransparent(rcClient, font, static_cast<XYPOSITION>(ytext),
s+startSeg, endSeg - startSeg,
highlight ? colourSel : colourUnSel);
}
}
x = xEnd;
startSeg = endSeg;
}
}
}
int CallTip::PaintContents(Surface *surfaceWindow, bool draw) {
const PRectangle rcClientPos = wCallTip.GetClientPosition();
const PRectangle rcClientSize(0.0f, 0.0f, rcClientPos.right - rcClientPos.left,
rcClientPos.bottom - rcClientPos.top);
PRectangle rcClient(1.0f, 1.0f, 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
const int ascent = static_cast<int>(lround(surfaceWindow->Ascent(font) - surfaceWindow->InternalLeading(font)));
// For each line...
// Draw the definition in three parts: before highlight, highlighted, after highlight
int ytext = static_cast<int>(rcClient.top) + ascent + 1;
rcClient.bottom = ytext + surfaceWindow->Descent(font) + 1;
const char *chunkVal = val.c_str();
bool moreChunks = true;
int maxWidth = 0;
while (moreChunks) {
const char *chunkEnd = strchr(chunkVal, '\n');
if (!chunkEnd) {
chunkEnd = chunkVal + strlen(chunkVal);
moreChunks = false;
}
const int chunkOffset = static_cast<int>(chunkVal - val.c_str());
const int chunkLength = static_cast<int>(chunkEnd - chunkVal);
const int chunkEndOffset = chunkOffset + chunkLength;
int thisStartHighlight = std::max(startHighlight, chunkOffset);
thisStartHighlight = std::min(thisStartHighlight, chunkEndOffset);
thisStartHighlight -= chunkOffset;
int thisEndHighlight = std::max(endHighlight, chunkOffset);
thisEndHighlight = std::min(thisEndHighlight, chunkEndOffset);
thisEndHighlight -= chunkOffset;
rcClient.top = static_cast<XYPOSITION>(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 = std::max(maxWidth, x);
}
return maxWidth;
}
void CallTip::PaintCT(Surface *surfaceWindow) {
if (val.empty())
return;
const PRectangle rcClientPos = wCallTip.GetClientPosition();
const PRectangle rcClientSize(0.0f, 0.0f, rcClientPos.right - rcClientPos.left,
rcClientPos.bottom - rcClientPos.top);
const PRectangle rcClient(1.0f, 1.0f, rcClientSize.right - 1, rcClientSize.bottom - 1);
surfaceWindow->FillRectangle(rcClient, colourBG);
offsetMain = insetX; // initial alignment assuming no arrows
PaintContents(surfaceWindow, true);
#ifndef __APPLE__
// OSX doesn't put borders on "help tags"
// Draw a raised border around the edges of the window
const IntegerRectangle ircClientSize(rcClientSize);
surfaceWindow->MoveTo(0, ircClientSize.bottom - 1);
surfaceWindow->PenColour(colourShade);
surfaceWindow->LineTo(ircClientSize.right - 1, ircClientSize.bottom - 1);
surfaceWindow->LineTo(ircClientSize.right - 1, 0);
surfaceWindow->PenColour(colourLight);
surfaceWindow->LineTo(0, 0);
surfaceWindow->LineTo(0, ircClientSize.bottom - 1);
#endif
}
void CallTip::MouseClick(Point pt) {
clickPlace = 0;
if (rectUp.Contains(pt))
clickPlace = 1;
if (rectDown.Contains(pt))
clickPlace = 2;
}
PRectangle CallTip::CallTipStart(Sci::Position pos, Point pt, int textHeight, const char *defn,
const char *faceName, int size,
int codePage_, int characterSet,
int technology, const Window &wParent) {
clickPlace = 0;
val = defn;
codePage = codePage_;
std::unique_ptr<Surface> surfaceMeasure(Surface::Allocate(technology));
surfaceMeasure->Init(wParent.GetID());
surfaceMeasure->SetUnicodeMode(SC_CP_UTF8 == codePage);
surfaceMeasure->SetDBCSMode(codePage);
startHighlight = 0;
endHighlight = 0;
inCallTipMode = true;
posStartCallTip = pos;
const XYPOSITION deviceHeight = static_cast<XYPOSITION>(surfaceMeasure->DeviceHeightFont(size));
const FontParameters fp(faceName, deviceHeight / SC_FONT_SIZE_MULTIPLIER, SC_WEIGHT_NORMAL, false, 0, technology, characterSet);
font.Create(fp);
// Look for multiple lines in the text
// Only support \n here - simply means container must avoid \r!
const int numLines = 1 + static_cast<int>(std::count(val.begin(), val.end(), '\n'));
rectUp = PRectangle(0,0,0,0);
rectDown = PRectangle(0,0,0,0);
offsetMain = insetX; // changed to right edge of any arrows
const int width = PaintContents(surfaceMeasure.get(), false) + insetX;
lineHeight = static_cast<int>(lround(surfaceMeasure->Height(font)));
// 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.
const int height = lineHeight * numLines - static_cast<int>(surfaceMeasure->InternalLeading(font)) + borderHeight * 2;
if (above) {
return PRectangle(pt.x - offsetMain, pt.y - verticalOffset - height, pt.x + width - offsetMain, pt.y - verticalOffset);
} else {
return PRectangle(pt.x - offsetMain, pt.y + verticalOffset + textHeight, pt.x + width - offsetMain, pt.y + verticalOffset + textHeight + 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 > start) ? end : start;
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;
}
// Set the calltip position, below the text by default or if above is false
// else above the text.
void CallTip::SetPosition(bool aboveText) {
above = aboveText;
}
// It might be better to have two access functions for this and to use
// them for all settings of colours.
void CallTip::SetForeBack(const ColourDesired &fore, const ColourDesired &back) {
colourBG = back;
colourUnSel = fore;
}

View File

@@ -0,0 +1,91 @@
// 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
namespace Scintilla {
/**
*/
class CallTip {
int startHighlight; // character offset to start and...
int endHighlight; // ...end of highlighted text
std::string 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
bool above; // if true, display calltip above text
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 ch) const;
int NextTabPos(int x) const;
public:
Window wCallTip;
Window wDraw;
bool inCallTipMode;
Sci::Position posStartCallTip;
ColourDesired colourBG;
ColourDesired colourUnSel;
ColourDesired colourSel;
ColourDesired colourShade;
ColourDesired colourLight;
int codePage;
int clickPlace;
int insetX; // text inset in x from calltip border
int widthArrow;
int borderHeight;
int verticalOffset; // pixel offset up or down of the calltip with respect to the line
CallTip();
// Deleted so CallTip objects can not be copied.
CallTip(const CallTip &) = delete;
CallTip(CallTip &&) = delete;
CallTip &operator=(const CallTip &) = delete;
CallTip &operator=(CallTip &&) = delete;
~CallTip();
void PaintCT(Surface *surfaceWindow);
void MouseClick(Point pt);
/// Setup the calltip and return a rectangle of the area required.
PRectangle CallTipStart(Sci::Position pos, Point pt, int textHeight, const char *defn,
const char *faceName, int size, int codePage_,
int characterSet, int technology, const 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);
/// Set calltip position.
void SetPosition(bool aboveText);
/// 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 ColourDesired &fore, const ColourDesired &back);
};
}
#endif

View File

@@ -0,0 +1,819 @@
// Scintilla source code edit control
// Encoding: UTF-8
/** @file CaseConvert.cxx
** Case fold characters and convert them to upper or lower case.
** Tables automatically regenerated by scripts/GenerateCaseConvert.py
** Should only be rarely regenerated for new versions of Unicode.
**/
// Copyright 2013 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstring>
#include <stdexcept>
#include <string>
#include <vector>
#include <algorithm>
#include "StringCopy.h"
#include "CaseConvert.h"
#include "UniConversion.h"
using namespace Scintilla;
namespace {
// Use an unnamed namespace to protect the declarations from name conflicts
// Unicode code points are ordered by groups and follow patterns.
// Most characters (pitch==1) are in ranges for a particular alphabet and their
// upper case forms are a fixed distance away.
// Another pattern (pitch==2) is where each lower case letter is preceded by
// the upper case form. These are also grouped into ranges.
int symmetricCaseConversionRanges[] = {
//lower, upper, range length, range pitch
//++Autogenerated -- start of section automatically generated
//**\(\*\n\)
97,65,26,1,
224,192,23,1,
248,216,7,1,
257,256,24,2,
314,313,8,2,
331,330,23,2,
462,461,8,2,
479,478,9,2,
505,504,20,2,
547,546,9,2,
583,582,5,2,
945,913,17,1,
963,931,9,1,
985,984,12,2,
1072,1040,32,1,
1104,1024,16,1,
1121,1120,17,2,
1163,1162,27,2,
1218,1217,7,2,
1233,1232,48,2,
1377,1329,38,1,
4304,7312,43,1,
7681,7680,75,2,
7841,7840,48,2,
7936,7944,8,1,
7952,7960,6,1,
7968,7976,8,1,
7984,7992,8,1,
8000,8008,6,1,
8032,8040,8,1,
8560,8544,16,1,
9424,9398,26,1,
11312,11264,47,1,
11393,11392,50,2,
11520,4256,38,1,
42561,42560,23,2,
42625,42624,14,2,
42787,42786,7,2,
42803,42802,31,2,
42879,42878,5,2,
42903,42902,10,2,
65345,65313,26,1,
66600,66560,40,1,
66776,66736,36,1,
68800,68736,51,1,
71872,71840,32,1,
93792,93760,32,1,
125218,125184,34,1,
//--Autogenerated -- end of section automatically generated
};
// Code points that are symmetric but don't fit into a range of similar characters
// are listed here.
int symmetricCaseConversions[] = {
//lower, upper
//++Autogenerated -- start of section automatically generated
//**1 \(\*\n\)
255,376,
307,306,
309,308,
311,310,
378,377,
380,379,
382,381,
384,579,
387,386,
389,388,
392,391,
396,395,
402,401,
405,502,
409,408,
410,573,
414,544,
417,416,
419,418,
421,420,
424,423,
429,428,
432,431,
436,435,
438,437,
441,440,
445,444,
447,503,
454,452,
457,455,
460,458,
477,398,
499,497,
501,500,
572,571,
575,11390,
576,11391,
578,577,
592,11375,
593,11373,
594,11376,
595,385,
596,390,
598,393,
599,394,
601,399,
603,400,
604,42923,
608,403,
609,42924,
611,404,
613,42893,
614,42922,
616,407,
617,406,
618,42926,
619,11362,
620,42925,
623,412,
625,11374,
626,413,
629,415,
637,11364,
640,422,
643,425,
647,42929,
648,430,
649,580,
650,433,
651,434,
652,581,
658,439,
669,42930,
670,42928,
881,880,
883,882,
887,886,
891,1021,
892,1022,
893,1023,
940,902,
941,904,
942,905,
943,906,
972,908,
973,910,
974,911,
983,975,
1010,1017,
1011,895,
1016,1015,
1019,1018,
1231,1216,
4349,7357,
4350,7358,
4351,7359,
7545,42877,
7549,11363,
8017,8025,
8019,8027,
8021,8029,
8023,8031,
8048,8122,
8049,8123,
8050,8136,
8051,8137,
8052,8138,
8053,8139,
8054,8154,
8055,8155,
8056,8184,
8057,8185,
8058,8170,
8059,8171,
8060,8186,
8061,8187,
8112,8120,
8113,8121,
8144,8152,
8145,8153,
8160,8168,
8161,8169,
8165,8172,
8526,8498,
8580,8579,
11361,11360,
11365,570,
11366,574,
11368,11367,
11370,11369,
11372,11371,
11379,11378,
11382,11381,
11500,11499,
11502,11501,
11507,11506,
11559,4295,
11565,4301,
42874,42873,
42876,42875,
42892,42891,
42897,42896,
42899,42898,
42933,42932,
42935,42934,
42937,42936,
43859,42931,
//--Autogenerated -- end of section automatically generated
};
// Characters that have complex case conversions are listed here.
// This includes cases where more than one character is needed for a conversion,
// folding is different to lowering, or (as appropriate) upper(lower(x)) != x or
// lower(upper(x)) != x.
const char *complexCaseConversions =
// Original | Folded | Upper | Lower |
//++Autogenerated -- start of section automatically generated
//**2 \(\*\n\)
"\xc2\xb5|\xce\xbc|\xce\x9c||"
"\xc3\x9f|ss|SS||"
"\xc4\xb0|i\xcc\x87||i\xcc\x87|"
"\xc4\xb1||I||"
"\xc5\x89|\xca\xbcn|\xca\xbcN||"
"\xc5\xbf|s|S||"
"\xc7\x85|\xc7\x86|\xc7\x84|\xc7\x86|"
"\xc7\x88|\xc7\x89|\xc7\x87|\xc7\x89|"
"\xc7\x8b|\xc7\x8c|\xc7\x8a|\xc7\x8c|"
"\xc7\xb0|j\xcc\x8c|J\xcc\x8c||"
"\xc7\xb2|\xc7\xb3|\xc7\xb1|\xc7\xb3|"
"\xcd\x85|\xce\xb9|\xce\x99||"
"\xce\x90|\xce\xb9\xcc\x88\xcc\x81|\xce\x99\xcc\x88\xcc\x81||"
"\xce\xb0|\xcf\x85\xcc\x88\xcc\x81|\xce\xa5\xcc\x88\xcc\x81||"
"\xcf\x82|\xcf\x83|\xce\xa3||"
"\xcf\x90|\xce\xb2|\xce\x92||"
"\xcf\x91|\xce\xb8|\xce\x98||"
"\xcf\x95|\xcf\x86|\xce\xa6||"
"\xcf\x96|\xcf\x80|\xce\xa0||"
"\xcf\xb0|\xce\xba|\xce\x9a||"
"\xcf\xb1|\xcf\x81|\xce\xa1||"
"\xcf\xb4|\xce\xb8||\xce\xb8|"
"\xcf\xb5|\xce\xb5|\xce\x95||"
"\xd6\x87|\xd5\xa5\xd6\x82|\xd4\xb5\xd5\x92||"
"\xe1\x8e\xa0|||\xea\xad\xb0|"
"\xe1\x8e\xa1|||\xea\xad\xb1|"
"\xe1\x8e\xa2|||\xea\xad\xb2|"
"\xe1\x8e\xa3|||\xea\xad\xb3|"
"\xe1\x8e\xa4|||\xea\xad\xb4|"
"\xe1\x8e\xa5|||\xea\xad\xb5|"
"\xe1\x8e\xa6|||\xea\xad\xb6|"
"\xe1\x8e\xa7|||\xea\xad\xb7|"
"\xe1\x8e\xa8|||\xea\xad\xb8|"
"\xe1\x8e\xa9|||\xea\xad\xb9|"
"\xe1\x8e\xaa|||\xea\xad\xba|"
"\xe1\x8e\xab|||\xea\xad\xbb|"
"\xe1\x8e\xac|||\xea\xad\xbc|"
"\xe1\x8e\xad|||\xea\xad\xbd|"
"\xe1\x8e\xae|||\xea\xad\xbe|"
"\xe1\x8e\xaf|||\xea\xad\xbf|"
"\xe1\x8e\xb0|||\xea\xae\x80|"
"\xe1\x8e\xb1|||\xea\xae\x81|"
"\xe1\x8e\xb2|||\xea\xae\x82|"
"\xe1\x8e\xb3|||\xea\xae\x83|"
"\xe1\x8e\xb4|||\xea\xae\x84|"
"\xe1\x8e\xb5|||\xea\xae\x85|"
"\xe1\x8e\xb6|||\xea\xae\x86|"
"\xe1\x8e\xb7|||\xea\xae\x87|"
"\xe1\x8e\xb8|||\xea\xae\x88|"
"\xe1\x8e\xb9|||\xea\xae\x89|"
"\xe1\x8e\xba|||\xea\xae\x8a|"
"\xe1\x8e\xbb|||\xea\xae\x8b|"
"\xe1\x8e\xbc|||\xea\xae\x8c|"
"\xe1\x8e\xbd|||\xea\xae\x8d|"
"\xe1\x8e\xbe|||\xea\xae\x8e|"
"\xe1\x8e\xbf|||\xea\xae\x8f|"
"\xe1\x8f\x80|||\xea\xae\x90|"
"\xe1\x8f\x81|||\xea\xae\x91|"
"\xe1\x8f\x82|||\xea\xae\x92|"
"\xe1\x8f\x83|||\xea\xae\x93|"
"\xe1\x8f\x84|||\xea\xae\x94|"
"\xe1\x8f\x85|||\xea\xae\x95|"
"\xe1\x8f\x86|||\xea\xae\x96|"
"\xe1\x8f\x87|||\xea\xae\x97|"
"\xe1\x8f\x88|||\xea\xae\x98|"
"\xe1\x8f\x89|||\xea\xae\x99|"
"\xe1\x8f\x8a|||\xea\xae\x9a|"
"\xe1\x8f\x8b|||\xea\xae\x9b|"
"\xe1\x8f\x8c|||\xea\xae\x9c|"
"\xe1\x8f\x8d|||\xea\xae\x9d|"
"\xe1\x8f\x8e|||\xea\xae\x9e|"
"\xe1\x8f\x8f|||\xea\xae\x9f|"
"\xe1\x8f\x90|||\xea\xae\xa0|"
"\xe1\x8f\x91|||\xea\xae\xa1|"
"\xe1\x8f\x92|||\xea\xae\xa2|"
"\xe1\x8f\x93|||\xea\xae\xa3|"
"\xe1\x8f\x94|||\xea\xae\xa4|"
"\xe1\x8f\x95|||\xea\xae\xa5|"
"\xe1\x8f\x96|||\xea\xae\xa6|"
"\xe1\x8f\x97|||\xea\xae\xa7|"
"\xe1\x8f\x98|||\xea\xae\xa8|"
"\xe1\x8f\x99|||\xea\xae\xa9|"
"\xe1\x8f\x9a|||\xea\xae\xaa|"
"\xe1\x8f\x9b|||\xea\xae\xab|"
"\xe1\x8f\x9c|||\xea\xae\xac|"
"\xe1\x8f\x9d|||\xea\xae\xad|"
"\xe1\x8f\x9e|||\xea\xae\xae|"
"\xe1\x8f\x9f|||\xea\xae\xaf|"
"\xe1\x8f\xa0|||\xea\xae\xb0|"
"\xe1\x8f\xa1|||\xea\xae\xb1|"
"\xe1\x8f\xa2|||\xea\xae\xb2|"
"\xe1\x8f\xa3|||\xea\xae\xb3|"
"\xe1\x8f\xa4|||\xea\xae\xb4|"
"\xe1\x8f\xa5|||\xea\xae\xb5|"
"\xe1\x8f\xa6|||\xea\xae\xb6|"
"\xe1\x8f\xa7|||\xea\xae\xb7|"
"\xe1\x8f\xa8|||\xea\xae\xb8|"
"\xe1\x8f\xa9|||\xea\xae\xb9|"
"\xe1\x8f\xaa|||\xea\xae\xba|"
"\xe1\x8f\xab|||\xea\xae\xbb|"
"\xe1\x8f\xac|||\xea\xae\xbc|"
"\xe1\x8f\xad|||\xea\xae\xbd|"
"\xe1\x8f\xae|||\xea\xae\xbe|"
"\xe1\x8f\xaf|||\xea\xae\xbf|"
"\xe1\x8f\xb0|||\xe1\x8f\xb8|"
"\xe1\x8f\xb1|||\xe1\x8f\xb9|"
"\xe1\x8f\xb2|||\xe1\x8f\xba|"
"\xe1\x8f\xb3|||\xe1\x8f\xbb|"
"\xe1\x8f\xb4|||\xe1\x8f\xbc|"
"\xe1\x8f\xb5|||\xe1\x8f\xbd|"
"\xe1\x8f\xb8|\xe1\x8f\xb0|\xe1\x8f\xb0||"
"\xe1\x8f\xb9|\xe1\x8f\xb1|\xe1\x8f\xb1||"
"\xe1\x8f\xba|\xe1\x8f\xb2|\xe1\x8f\xb2||"
"\xe1\x8f\xbb|\xe1\x8f\xb3|\xe1\x8f\xb3||"
"\xe1\x8f\xbc|\xe1\x8f\xb4|\xe1\x8f\xb4||"
"\xe1\x8f\xbd|\xe1\x8f\xb5|\xe1\x8f\xb5||"
"\xe1\xb2\x80|\xd0\xb2|\xd0\x92||"
"\xe1\xb2\x81|\xd0\xb4|\xd0\x94||"
"\xe1\xb2\x82|\xd0\xbe|\xd0\x9e||"
"\xe1\xb2\x83|\xd1\x81|\xd0\xa1||"
"\xe1\xb2\x84|\xd1\x82|\xd0\xa2||"
"\xe1\xb2\x85|\xd1\x82|\xd0\xa2||"
"\xe1\xb2\x86|\xd1\x8a|\xd0\xaa||"
"\xe1\xb2\x87|\xd1\xa3|\xd1\xa2||"
"\xe1\xb2\x88|\xea\x99\x8b|\xea\x99\x8a||"
"\xe1\xba\x96|h\xcc\xb1|H\xcc\xb1||"
"\xe1\xba\x97|t\xcc\x88|T\xcc\x88||"
"\xe1\xba\x98|w\xcc\x8a|W\xcc\x8a||"
"\xe1\xba\x99|y\xcc\x8a|Y\xcc\x8a||"
"\xe1\xba\x9a|a\xca\xbe|A\xca\xbe||"
"\xe1\xba\x9b|\xe1\xb9\xa1|\xe1\xb9\xa0||"
"\xe1\xba\x9e|ss||\xc3\x9f|"
"\xe1\xbd\x90|\xcf\x85\xcc\x93|\xce\xa5\xcc\x93||"
"\xe1\xbd\x92|\xcf\x85\xcc\x93\xcc\x80|\xce\xa5\xcc\x93\xcc\x80||"
"\xe1\xbd\x94|\xcf\x85\xcc\x93\xcc\x81|\xce\xa5\xcc\x93\xcc\x81||"
"\xe1\xbd\x96|\xcf\x85\xcc\x93\xcd\x82|\xce\xa5\xcc\x93\xcd\x82||"
"\xe1\xbe\x80|\xe1\xbc\x80\xce\xb9|\xe1\xbc\x88\xce\x99||"
"\xe1\xbe\x81|\xe1\xbc\x81\xce\xb9|\xe1\xbc\x89\xce\x99||"
"\xe1\xbe\x82|\xe1\xbc\x82\xce\xb9|\xe1\xbc\x8a\xce\x99||"
"\xe1\xbe\x83|\xe1\xbc\x83\xce\xb9|\xe1\xbc\x8b\xce\x99||"
"\xe1\xbe\x84|\xe1\xbc\x84\xce\xb9|\xe1\xbc\x8c\xce\x99||"
"\xe1\xbe\x85|\xe1\xbc\x85\xce\xb9|\xe1\xbc\x8d\xce\x99||"
"\xe1\xbe\x86|\xe1\xbc\x86\xce\xb9|\xe1\xbc\x8e\xce\x99||"
"\xe1\xbe\x87|\xe1\xbc\x87\xce\xb9|\xe1\xbc\x8f\xce\x99||"
"\xe1\xbe\x88|\xe1\xbc\x80\xce\xb9|\xe1\xbc\x88\xce\x99|\xe1\xbe\x80|"
"\xe1\xbe\x89|\xe1\xbc\x81\xce\xb9|\xe1\xbc\x89\xce\x99|\xe1\xbe\x81|"
"\xe1\xbe\x8a|\xe1\xbc\x82\xce\xb9|\xe1\xbc\x8a\xce\x99|\xe1\xbe\x82|"
"\xe1\xbe\x8b|\xe1\xbc\x83\xce\xb9|\xe1\xbc\x8b\xce\x99|\xe1\xbe\x83|"
"\xe1\xbe\x8c|\xe1\xbc\x84\xce\xb9|\xe1\xbc\x8c\xce\x99|\xe1\xbe\x84|"
"\xe1\xbe\x8d|\xe1\xbc\x85\xce\xb9|\xe1\xbc\x8d\xce\x99|\xe1\xbe\x85|"
"\xe1\xbe\x8e|\xe1\xbc\x86\xce\xb9|\xe1\xbc\x8e\xce\x99|\xe1\xbe\x86|"
"\xe1\xbe\x8f|\xe1\xbc\x87\xce\xb9|\xe1\xbc\x8f\xce\x99|\xe1\xbe\x87|"
"\xe1\xbe\x90|\xe1\xbc\xa0\xce\xb9|\xe1\xbc\xa8\xce\x99||"
"\xe1\xbe\x91|\xe1\xbc\xa1\xce\xb9|\xe1\xbc\xa9\xce\x99||"
"\xe1\xbe\x92|\xe1\xbc\xa2\xce\xb9|\xe1\xbc\xaa\xce\x99||"
"\xe1\xbe\x93|\xe1\xbc\xa3\xce\xb9|\xe1\xbc\xab\xce\x99||"
"\xe1\xbe\x94|\xe1\xbc\xa4\xce\xb9|\xe1\xbc\xac\xce\x99||"
"\xe1\xbe\x95|\xe1\xbc\xa5\xce\xb9|\xe1\xbc\xad\xce\x99||"
"\xe1\xbe\x96|\xe1\xbc\xa6\xce\xb9|\xe1\xbc\xae\xce\x99||"
"\xe1\xbe\x97|\xe1\xbc\xa7\xce\xb9|\xe1\xbc\xaf\xce\x99||"
"\xe1\xbe\x98|\xe1\xbc\xa0\xce\xb9|\xe1\xbc\xa8\xce\x99|\xe1\xbe\x90|"
"\xe1\xbe\x99|\xe1\xbc\xa1\xce\xb9|\xe1\xbc\xa9\xce\x99|\xe1\xbe\x91|"
"\xe1\xbe\x9a|\xe1\xbc\xa2\xce\xb9|\xe1\xbc\xaa\xce\x99|\xe1\xbe\x92|"
"\xe1\xbe\x9b|\xe1\xbc\xa3\xce\xb9|\xe1\xbc\xab\xce\x99|\xe1\xbe\x93|"
"\xe1\xbe\x9c|\xe1\xbc\xa4\xce\xb9|\xe1\xbc\xac\xce\x99|\xe1\xbe\x94|"
"\xe1\xbe\x9d|\xe1\xbc\xa5\xce\xb9|\xe1\xbc\xad\xce\x99|\xe1\xbe\x95|"
"\xe1\xbe\x9e|\xe1\xbc\xa6\xce\xb9|\xe1\xbc\xae\xce\x99|\xe1\xbe\x96|"
"\xe1\xbe\x9f|\xe1\xbc\xa7\xce\xb9|\xe1\xbc\xaf\xce\x99|\xe1\xbe\x97|"
"\xe1\xbe\xa0|\xe1\xbd\xa0\xce\xb9|\xe1\xbd\xa8\xce\x99||"
"\xe1\xbe\xa1|\xe1\xbd\xa1\xce\xb9|\xe1\xbd\xa9\xce\x99||"
"\xe1\xbe\xa2|\xe1\xbd\xa2\xce\xb9|\xe1\xbd\xaa\xce\x99||"
"\xe1\xbe\xa3|\xe1\xbd\xa3\xce\xb9|\xe1\xbd\xab\xce\x99||"
"\xe1\xbe\xa4|\xe1\xbd\xa4\xce\xb9|\xe1\xbd\xac\xce\x99||"
"\xe1\xbe\xa5|\xe1\xbd\xa5\xce\xb9|\xe1\xbd\xad\xce\x99||"
"\xe1\xbe\xa6|\xe1\xbd\xa6\xce\xb9|\xe1\xbd\xae\xce\x99||"
"\xe1\xbe\xa7|\xe1\xbd\xa7\xce\xb9|\xe1\xbd\xaf\xce\x99||"
"\xe1\xbe\xa8|\xe1\xbd\xa0\xce\xb9|\xe1\xbd\xa8\xce\x99|\xe1\xbe\xa0|"
"\xe1\xbe\xa9|\xe1\xbd\xa1\xce\xb9|\xe1\xbd\xa9\xce\x99|\xe1\xbe\xa1|"
"\xe1\xbe\xaa|\xe1\xbd\xa2\xce\xb9|\xe1\xbd\xaa\xce\x99|\xe1\xbe\xa2|"
"\xe1\xbe\xab|\xe1\xbd\xa3\xce\xb9|\xe1\xbd\xab\xce\x99|\xe1\xbe\xa3|"
"\xe1\xbe\xac|\xe1\xbd\xa4\xce\xb9|\xe1\xbd\xac\xce\x99|\xe1\xbe\xa4|"
"\xe1\xbe\xad|\xe1\xbd\xa5\xce\xb9|\xe1\xbd\xad\xce\x99|\xe1\xbe\xa5|"
"\xe1\xbe\xae|\xe1\xbd\xa6\xce\xb9|\xe1\xbd\xae\xce\x99|\xe1\xbe\xa6|"
"\xe1\xbe\xaf|\xe1\xbd\xa7\xce\xb9|\xe1\xbd\xaf\xce\x99|\xe1\xbe\xa7|"
"\xe1\xbe\xb2|\xe1\xbd\xb0\xce\xb9|\xe1\xbe\xba\xce\x99||"
"\xe1\xbe\xb3|\xce\xb1\xce\xb9|\xce\x91\xce\x99||"
"\xe1\xbe\xb4|\xce\xac\xce\xb9|\xce\x86\xce\x99||"
"\xe1\xbe\xb6|\xce\xb1\xcd\x82|\xce\x91\xcd\x82||"
"\xe1\xbe\xb7|\xce\xb1\xcd\x82\xce\xb9|\xce\x91\xcd\x82\xce\x99||"
"\xe1\xbe\xbc|\xce\xb1\xce\xb9|\xce\x91\xce\x99|\xe1\xbe\xb3|"
"\xe1\xbe\xbe|\xce\xb9|\xce\x99||"
"\xe1\xbf\x82|\xe1\xbd\xb4\xce\xb9|\xe1\xbf\x8a\xce\x99||"
"\xe1\xbf\x83|\xce\xb7\xce\xb9|\xce\x97\xce\x99||"
"\xe1\xbf\x84|\xce\xae\xce\xb9|\xce\x89\xce\x99||"
"\xe1\xbf\x86|\xce\xb7\xcd\x82|\xce\x97\xcd\x82||"
"\xe1\xbf\x87|\xce\xb7\xcd\x82\xce\xb9|\xce\x97\xcd\x82\xce\x99||"
"\xe1\xbf\x8c|\xce\xb7\xce\xb9|\xce\x97\xce\x99|\xe1\xbf\x83|"
"\xe1\xbf\x92|\xce\xb9\xcc\x88\xcc\x80|\xce\x99\xcc\x88\xcc\x80||"
"\xe1\xbf\x93|\xce\xb9\xcc\x88\xcc\x81|\xce\x99\xcc\x88\xcc\x81||"
"\xe1\xbf\x96|\xce\xb9\xcd\x82|\xce\x99\xcd\x82||"
"\xe1\xbf\x97|\xce\xb9\xcc\x88\xcd\x82|\xce\x99\xcc\x88\xcd\x82||"
"\xe1\xbf\xa2|\xcf\x85\xcc\x88\xcc\x80|\xce\xa5\xcc\x88\xcc\x80||"
"\xe1\xbf\xa3|\xcf\x85\xcc\x88\xcc\x81|\xce\xa5\xcc\x88\xcc\x81||"
"\xe1\xbf\xa4|\xcf\x81\xcc\x93|\xce\xa1\xcc\x93||"
"\xe1\xbf\xa6|\xcf\x85\xcd\x82|\xce\xa5\xcd\x82||"
"\xe1\xbf\xa7|\xcf\x85\xcc\x88\xcd\x82|\xce\xa5\xcc\x88\xcd\x82||"
"\xe1\xbf\xb2|\xe1\xbd\xbc\xce\xb9|\xe1\xbf\xba\xce\x99||"
"\xe1\xbf\xb3|\xcf\x89\xce\xb9|\xce\xa9\xce\x99||"
"\xe1\xbf\xb4|\xcf\x8e\xce\xb9|\xce\x8f\xce\x99||"
"\xe1\xbf\xb6|\xcf\x89\xcd\x82|\xce\xa9\xcd\x82||"
"\xe1\xbf\xb7|\xcf\x89\xcd\x82\xce\xb9|\xce\xa9\xcd\x82\xce\x99||"
"\xe1\xbf\xbc|\xcf\x89\xce\xb9|\xce\xa9\xce\x99|\xe1\xbf\xb3|"
"\xe2\x84\xa6|\xcf\x89||\xcf\x89|"
"\xe2\x84\xaa|k||k|"
"\xe2\x84\xab|\xc3\xa5||\xc3\xa5|"
"\xea\xad\xb0|\xe1\x8e\xa0|\xe1\x8e\xa0||"
"\xea\xad\xb1|\xe1\x8e\xa1|\xe1\x8e\xa1||"
"\xea\xad\xb2|\xe1\x8e\xa2|\xe1\x8e\xa2||"
"\xea\xad\xb3|\xe1\x8e\xa3|\xe1\x8e\xa3||"
"\xea\xad\xb4|\xe1\x8e\xa4|\xe1\x8e\xa4||"
"\xea\xad\xb5|\xe1\x8e\xa5|\xe1\x8e\xa5||"
"\xea\xad\xb6|\xe1\x8e\xa6|\xe1\x8e\xa6||"
"\xea\xad\xb7|\xe1\x8e\xa7|\xe1\x8e\xa7||"
"\xea\xad\xb8|\xe1\x8e\xa8|\xe1\x8e\xa8||"
"\xea\xad\xb9|\xe1\x8e\xa9|\xe1\x8e\xa9||"
"\xea\xad\xba|\xe1\x8e\xaa|\xe1\x8e\xaa||"
"\xea\xad\xbb|\xe1\x8e\xab|\xe1\x8e\xab||"
"\xea\xad\xbc|\xe1\x8e\xac|\xe1\x8e\xac||"
"\xea\xad\xbd|\xe1\x8e\xad|\xe1\x8e\xad||"
"\xea\xad\xbe|\xe1\x8e\xae|\xe1\x8e\xae||"
"\xea\xad\xbf|\xe1\x8e\xaf|\xe1\x8e\xaf||"
"\xea\xae\x80|\xe1\x8e\xb0|\xe1\x8e\xb0||"
"\xea\xae\x81|\xe1\x8e\xb1|\xe1\x8e\xb1||"
"\xea\xae\x82|\xe1\x8e\xb2|\xe1\x8e\xb2||"
"\xea\xae\x83|\xe1\x8e\xb3|\xe1\x8e\xb3||"
"\xea\xae\x84|\xe1\x8e\xb4|\xe1\x8e\xb4||"
"\xea\xae\x85|\xe1\x8e\xb5|\xe1\x8e\xb5||"
"\xea\xae\x86|\xe1\x8e\xb6|\xe1\x8e\xb6||"
"\xea\xae\x87|\xe1\x8e\xb7|\xe1\x8e\xb7||"
"\xea\xae\x88|\xe1\x8e\xb8|\xe1\x8e\xb8||"
"\xea\xae\x89|\xe1\x8e\xb9|\xe1\x8e\xb9||"
"\xea\xae\x8a|\xe1\x8e\xba|\xe1\x8e\xba||"
"\xea\xae\x8b|\xe1\x8e\xbb|\xe1\x8e\xbb||"
"\xea\xae\x8c|\xe1\x8e\xbc|\xe1\x8e\xbc||"
"\xea\xae\x8d|\xe1\x8e\xbd|\xe1\x8e\xbd||"
"\xea\xae\x8e|\xe1\x8e\xbe|\xe1\x8e\xbe||"
"\xea\xae\x8f|\xe1\x8e\xbf|\xe1\x8e\xbf||"
"\xea\xae\x90|\xe1\x8f\x80|\xe1\x8f\x80||"
"\xea\xae\x91|\xe1\x8f\x81|\xe1\x8f\x81||"
"\xea\xae\x92|\xe1\x8f\x82|\xe1\x8f\x82||"
"\xea\xae\x93|\xe1\x8f\x83|\xe1\x8f\x83||"
"\xea\xae\x94|\xe1\x8f\x84|\xe1\x8f\x84||"
"\xea\xae\x95|\xe1\x8f\x85|\xe1\x8f\x85||"
"\xea\xae\x96|\xe1\x8f\x86|\xe1\x8f\x86||"
"\xea\xae\x97|\xe1\x8f\x87|\xe1\x8f\x87||"
"\xea\xae\x98|\xe1\x8f\x88|\xe1\x8f\x88||"
"\xea\xae\x99|\xe1\x8f\x89|\xe1\x8f\x89||"
"\xea\xae\x9a|\xe1\x8f\x8a|\xe1\x8f\x8a||"
"\xea\xae\x9b|\xe1\x8f\x8b|\xe1\x8f\x8b||"
"\xea\xae\x9c|\xe1\x8f\x8c|\xe1\x8f\x8c||"
"\xea\xae\x9d|\xe1\x8f\x8d|\xe1\x8f\x8d||"
"\xea\xae\x9e|\xe1\x8f\x8e|\xe1\x8f\x8e||"
"\xea\xae\x9f|\xe1\x8f\x8f|\xe1\x8f\x8f||"
"\xea\xae\xa0|\xe1\x8f\x90|\xe1\x8f\x90||"
"\xea\xae\xa1|\xe1\x8f\x91|\xe1\x8f\x91||"
"\xea\xae\xa2|\xe1\x8f\x92|\xe1\x8f\x92||"
"\xea\xae\xa3|\xe1\x8f\x93|\xe1\x8f\x93||"
"\xea\xae\xa4|\xe1\x8f\x94|\xe1\x8f\x94||"
"\xea\xae\xa5|\xe1\x8f\x95|\xe1\x8f\x95||"
"\xea\xae\xa6|\xe1\x8f\x96|\xe1\x8f\x96||"
"\xea\xae\xa7|\xe1\x8f\x97|\xe1\x8f\x97||"
"\xea\xae\xa8|\xe1\x8f\x98|\xe1\x8f\x98||"
"\xea\xae\xa9|\xe1\x8f\x99|\xe1\x8f\x99||"
"\xea\xae\xaa|\xe1\x8f\x9a|\xe1\x8f\x9a||"
"\xea\xae\xab|\xe1\x8f\x9b|\xe1\x8f\x9b||"
"\xea\xae\xac|\xe1\x8f\x9c|\xe1\x8f\x9c||"
"\xea\xae\xad|\xe1\x8f\x9d|\xe1\x8f\x9d||"
"\xea\xae\xae|\xe1\x8f\x9e|\xe1\x8f\x9e||"
"\xea\xae\xaf|\xe1\x8f\x9f|\xe1\x8f\x9f||"
"\xea\xae\xb0|\xe1\x8f\xa0|\xe1\x8f\xa0||"
"\xea\xae\xb1|\xe1\x8f\xa1|\xe1\x8f\xa1||"
"\xea\xae\xb2|\xe1\x8f\xa2|\xe1\x8f\xa2||"
"\xea\xae\xb3|\xe1\x8f\xa3|\xe1\x8f\xa3||"
"\xea\xae\xb4|\xe1\x8f\xa4|\xe1\x8f\xa4||"
"\xea\xae\xb5|\xe1\x8f\xa5|\xe1\x8f\xa5||"
"\xea\xae\xb6|\xe1\x8f\xa6|\xe1\x8f\xa6||"
"\xea\xae\xb7|\xe1\x8f\xa7|\xe1\x8f\xa7||"
"\xea\xae\xb8|\xe1\x8f\xa8|\xe1\x8f\xa8||"
"\xea\xae\xb9|\xe1\x8f\xa9|\xe1\x8f\xa9||"
"\xea\xae\xba|\xe1\x8f\xaa|\xe1\x8f\xaa||"
"\xea\xae\xbb|\xe1\x8f\xab|\xe1\x8f\xab||"
"\xea\xae\xbc|\xe1\x8f\xac|\xe1\x8f\xac||"
"\xea\xae\xbd|\xe1\x8f\xad|\xe1\x8f\xad||"
"\xea\xae\xbe|\xe1\x8f\xae|\xe1\x8f\xae||"
"\xea\xae\xbf|\xe1\x8f\xaf|\xe1\x8f\xaf||"
"\xef\xac\x80|ff|FF||"
"\xef\xac\x81|fi|FI||"
"\xef\xac\x82|fl|FL||"
"\xef\xac\x83|ffi|FFI||"
"\xef\xac\x84|ffl|FFL||"
"\xef\xac\x85|st|ST||"
"\xef\xac\x86|st|ST||"
"\xef\xac\x93|\xd5\xb4\xd5\xb6|\xd5\x84\xd5\x86||"
"\xef\xac\x94|\xd5\xb4\xd5\xa5|\xd5\x84\xd4\xb5||"
"\xef\xac\x95|\xd5\xb4\xd5\xab|\xd5\x84\xd4\xbb||"
"\xef\xac\x96|\xd5\xbe\xd5\xb6|\xd5\x8e\xd5\x86||"
"\xef\xac\x97|\xd5\xb4\xd5\xad|\xd5\x84\xd4\xbd||"
//--Autogenerated -- end of section automatically generated
;
class CaseConverter : public ICaseConverter {
// Maximum length of a case conversion result is 6 bytes in UTF-8
enum { maxConversionLength=6 };
struct ConversionString {
char conversion[maxConversionLength+1];
ConversionString() : conversion{} {
}
};
// Conversions are initially store in a vector of structs but then decomposed into
// parallel arrays as that is about 10% faster to search.
struct CharacterConversion {
int character;
ConversionString conversion;
CharacterConversion(int character_=0, const char *conversion_="") noexcept : character(character_) {
StringCopy(conversion.conversion, conversion_);
}
bool operator<(const CharacterConversion &other) const noexcept {
return character < other.character;
}
};
typedef std::vector<CharacterConversion> CharacterToConversion;
CharacterToConversion characterToConversion;
// The parallel arrays
std::vector<int> characters;
std::vector<ConversionString> conversions;
public:
CaseConverter() {
}
virtual ~CaseConverter() = default;
bool Initialised() const {
return characters.size() > 0;
}
void Add(int character, const char *conversion) {
characterToConversion.emplace_back(character, conversion);
}
const char *Find(int character) {
const std::vector<int>::iterator it = std::lower_bound(characters.begin(), characters.end(), character);
if (it == characters.end())
return nullptr;
else if (*it == character)
return conversions[it - characters.begin()].conversion;
else
return nullptr;
}
size_t CaseConvertString(char *converted, size_t sizeConverted, const char *mixed, size_t lenMixed) override {
size_t lenConverted = 0;
size_t mixedPos = 0;
unsigned char bytes[UTF8MaxBytes + 1]{};
while (mixedPos < lenMixed) {
const unsigned char leadByte = mixed[mixedPos];
const char *caseConverted = nullptr;
size_t lenMixedChar = 1;
if (UTF8IsAscii(leadByte)) {
caseConverted = Find(leadByte);
} else {
bytes[0] = leadByte;
const int widthCharBytes = UTF8BytesOfLead[leadByte];
for (int b=1; b<widthCharBytes; b++) {
bytes[b] = (mixedPos+b < lenMixed) ? mixed[mixedPos+b] : 0;
}
const int classified = UTF8Classify(bytes, widthCharBytes);
if (!(classified & UTF8MaskInvalid)) {
// valid UTF-8
lenMixedChar = classified & UTF8MaskWidth;
const int character = UnicodeFromUTF8(bytes);
caseConverted = Find(character);
}
}
if (caseConverted) {
// Character has a conversion so copy that conversion in
while (*caseConverted) {
converted[lenConverted++] = *caseConverted++;
if (lenConverted >= sizeConverted)
return 0;
}
} else {
// Character has no conversion so copy the input to output
for (size_t i=0; i<lenMixedChar; i++) {
converted[lenConverted++] = mixed[mixedPos+i];
if (lenConverted >= sizeConverted)
return 0;
}
}
mixedPos += lenMixedChar;
}
return lenConverted;
}
void FinishedAdding() {
std::sort(characterToConversion.begin(), characterToConversion.end());
characters.reserve(characterToConversion.size());
conversions.reserve(characterToConversion.size());
for (const CharacterConversion &chConv : characterToConversion) {
characters.push_back(chConv.character);
conversions.push_back(chConv.conversion);
}
// Empty the original calculated data completely
CharacterToConversion().swap(characterToConversion);
}
};
CaseConverter caseConvFold;
CaseConverter caseConvUp;
CaseConverter caseConvLow;
void AddSymmetric(enum CaseConversion conversion, int lower,int upper) {
char lowerUTF8[UTF8MaxBytes+1];
UTF8FromUTF32Character(lower, lowerUTF8);
char upperUTF8[UTF8MaxBytes+1];
UTF8FromUTF32Character(upper, upperUTF8);
switch (conversion) {
case CaseConversionFold:
caseConvFold.Add(upper, lowerUTF8);
break;
case CaseConversionUpper:
caseConvUp.Add(lower, upperUTF8);
break;
case CaseConversionLower:
caseConvLow.Add(upper, lowerUTF8);
break;
}
}
void SetupConversions(enum CaseConversion conversion) {
// First initialize for the symmetric ranges
for (size_t i=0; i<ELEMENTS(symmetricCaseConversionRanges);) {
const int lower = symmetricCaseConversionRanges[i++];
const int upper = symmetricCaseConversionRanges[i++];
const int length = symmetricCaseConversionRanges[i++];
const int pitch = symmetricCaseConversionRanges[i++];
for (int j=0; j<length*pitch; j+=pitch) {
AddSymmetric(conversion, lower+j, upper+j);
}
}
// Add the symmetric singletons
for (size_t i=0; i<ELEMENTS(symmetricCaseConversions);) {
const int lower = symmetricCaseConversions[i++];
const int upper = symmetricCaseConversions[i++];
AddSymmetric(conversion, lower, upper);
}
// Add the complex cases
const char *sComplex = complexCaseConversions;
while (*sComplex) {
// Longest ligature is 3 character so 5 for safety
const size_t lenUTF8 = 5*UTF8MaxBytes+1;
unsigned char originUTF8[lenUTF8]{};
char foldedUTF8[lenUTF8]{};
char lowerUTF8[lenUTF8]{};
char upperUTF8[lenUTF8]{};
size_t i = 0;
while (*sComplex && *sComplex != '|') {
originUTF8[i++] = *sComplex;
sComplex++;
}
sComplex++;
originUTF8[i] = 0;
i = 0;
while (*sComplex && *sComplex != '|') {
foldedUTF8[i++] = *sComplex;
sComplex++;
}
sComplex++;
foldedUTF8[i] = 0;
i = 0;
while (*sComplex && *sComplex != '|') {
upperUTF8[i++] = *sComplex;
sComplex++;
}
sComplex++;
upperUTF8[i] = 0;
i = 0;
while (*sComplex && *sComplex != '|') {
lowerUTF8[i++] = *sComplex;
sComplex++;
}
sComplex++;
lowerUTF8[i] = 0;
const int character = UnicodeFromUTF8(originUTF8);
if (conversion == CaseConversionFold && foldedUTF8[0]) {
caseConvFold.Add(character, foldedUTF8);
}
if (conversion == CaseConversionUpper && upperUTF8[0]) {
caseConvUp.Add(character, upperUTF8);
}
if (conversion == CaseConversionLower && lowerUTF8[0]) {
caseConvLow.Add(character, lowerUTF8);
}
}
switch (conversion) {
case CaseConversionFold:
caseConvFold.FinishedAdding();
break;
case CaseConversionUpper:
caseConvUp.FinishedAdding();
break;
case CaseConversionLower:
caseConvLow.FinishedAdding();
break;
}
}
CaseConverter *ConverterForConversion(enum CaseConversion conversion) {
switch (conversion) {
case CaseConversionFold:
return &caseConvFold;
case CaseConversionUpper:
return &caseConvUp;
case CaseConversionLower:
return &caseConvLow;
}
return nullptr;
}
}
namespace Scintilla {
ICaseConverter *ConverterFor(enum CaseConversion conversion) {
CaseConverter *pCaseConv = ConverterForConversion(conversion);
if (!pCaseConv->Initialised())
SetupConversions(conversion);
return pCaseConv;
}
const char *CaseConvert(int character, enum CaseConversion conversion) {
CaseConverter *pCaseConv = ConverterForConversion(conversion);
if (!pCaseConv->Initialised())
SetupConversions(conversion);
return pCaseConv->Find(character);
}
size_t CaseConvertString(char *converted, size_t sizeConverted, const char *mixed, size_t lenMixed, enum CaseConversion conversion) {
CaseConverter *pCaseConv = ConverterForConversion(conversion);
if (!pCaseConv->Initialised())
SetupConversions(conversion);
return pCaseConv->CaseConvertString(converted, sizeConverted, mixed, lenMixed);
}
std::string CaseConvertString(const std::string &s, enum CaseConversion conversion) {
std::string retMapped(s.length() * maxExpansionCaseConversion, 0);
const size_t lenMapped = CaseConvertString(&retMapped[0], retMapped.length(), s.c_str(), s.length(),
conversion);
retMapped.resize(lenMapped);
return retMapped;
}
}

View File

@@ -0,0 +1,46 @@
// Scintilla source code edit control
// Encoding: UTF-8
/** @file CaseConvert.h
** Performs Unicode case conversions.
** Does not handle locale-sensitive case conversion.
**/
// Copyright 2013 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef CASECONVERT_H
#define CASECONVERT_H
namespace Scintilla {
enum CaseConversion {
CaseConversionFold,
CaseConversionUpper,
CaseConversionLower
};
class ICaseConverter {
public:
virtual size_t CaseConvertString(char *converted, size_t sizeConverted, const char *mixed, size_t lenMixed) = 0;
};
ICaseConverter *ConverterFor(enum CaseConversion conversion);
// Returns a UTF-8 string. Empty when no conversion
const char *CaseConvert(int character, enum CaseConversion conversion);
// When performing CaseConvertString, the converted value may be up to 3 times longer than the input.
// Ligatures are often decomposed into multiple characters and long cases include:
// ΐ "\xce\x90" folds to ΐ "\xce\xb9\xcc\x88\xcc\x81"
const int maxExpansionCaseConversion=3;
// Converts a mixed case string using a particular conversion.
// Result may be a different length to input and the length is the return value.
// If there is not enough space then 0 is returned.
size_t CaseConvertString(char *converted, size_t sizeConverted, const char *mixed, size_t lenMixed, enum CaseConversion conversion);
// Converts a mixed case string using a particular conversion.
std::string CaseConvertString(const std::string &s, enum CaseConversion conversion);
}
#endif

View File

@@ -0,0 +1,66 @@
// Scintilla source code edit control
/** @file CaseFolder.cxx
** Classes for case folding.
**/
// Copyright 1998-2013 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <stdexcept>
#include <vector>
#include <algorithm>
#include "CaseFolder.h"
#include "CaseConvert.h"
using namespace Scintilla;
CaseFolder::~CaseFolder() {
}
CaseFolderTable::CaseFolderTable() : mapping{} {
for (size_t iChar=0; iChar<sizeof(mapping); iChar++) {
mapping[iChar] = static_cast<char>(iChar);
}
}
CaseFolderTable::~CaseFolderTable() {
}
size_t CaseFolderTable::Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
if (lenMixed > sizeFolded) {
return 0;
} else {
for (size_t i=0; i<lenMixed; i++) {
folded[i] = mapping[static_cast<unsigned char>(mixed[i])];
}
return lenMixed;
}
}
void CaseFolderTable::SetTranslation(char ch, char chTranslation) {
mapping[static_cast<unsigned char>(ch)] = chTranslation;
}
void CaseFolderTable::StandardASCII() {
for (size_t iChar=0; iChar<sizeof(mapping); iChar++) {
if (iChar >= 'A' && iChar <= 'Z') {
mapping[iChar] = static_cast<char>(iChar - 'A' + 'a');
} else {
mapping[iChar] = static_cast<char>(iChar);
}
}
}
CaseFolderUnicode::CaseFolderUnicode() {
StandardASCII();
converter = ConverterFor(CaseConversionFold);
}
size_t CaseFolderUnicode::Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) {
if ((lenMixed == 1) && (sizeFolded > 0)) {
folded[0] = mapping[static_cast<unsigned char>(mixed[0])];
return 1;
} else {
return converter->CaseConvertString(folded, sizeFolded, mixed, lenMixed);
}
}

View File

@@ -0,0 +1,41 @@
// Scintilla source code edit control
/** @file CaseFolder.h
** Classes for case folding.
**/
// Copyright 1998-2013 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef CASEFOLDER_H
#define CASEFOLDER_H
namespace Scintilla {
class CaseFolder {
public:
virtual ~CaseFolder();
virtual size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) = 0;
};
class CaseFolderTable : public CaseFolder {
protected:
char mapping[256];
public:
CaseFolderTable();
~CaseFolderTable() override;
size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) override;
void SetTranslation(char ch, char chTranslation);
void StandardASCII();
};
class ICaseConverter;
class CaseFolderUnicode : public CaseFolderTable {
ICaseConverter *converter;
public:
CaseFolderUnicode();
size_t Fold(char *folded, size_t sizeFolded, const char *mixed, size_t lenMixed) override;
};
}
#endif

View File

@@ -0,0 +1,204 @@
// Scintilla source code edit control
/** @file Catalogue.cxx
** Lexer infrastructure.
** Contains a list of LexerModules which can be searched to find a module appropriate for a
** particular language.
**/
// Copyright 1998-2002 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstdlib>
#include <cassert>
#include <cstring>
#include <stdexcept>
#include <vector>
#include "ILexer.h"
#include "Scintilla.h"
#include "SciLexer.h"
#include "LexerModule.h"
#include "Catalogue.h"
using namespace Scintilla;
static std::vector<LexerModule *> lexerCatalogue;
static int nextLanguage = SCLEX_AUTOMATIC+1;
const LexerModule *Catalogue::Find(int language) {
Scintilla_LinkLexers();
for (const LexerModule *lm : lexerCatalogue) {
if (lm->GetLanguage() == language) {
return lm;
}
}
return nullptr;
}
const LexerModule *Catalogue::Find(const char *languageName) {
Scintilla_LinkLexers();
if (languageName) {
for (const LexerModule *lm : lexerCatalogue) {
if (lm->languageName && (0 == strcmp(lm->languageName, languageName))) {
return lm;
}
}
}
return nullptr;
}
void Catalogue::AddLexerModule(LexerModule *plm) {
if (plm->GetLanguage() == SCLEX_AUTOMATIC) {
plm->language = nextLanguage;
nextLanguage++;
}
lexerCatalogue.push_back(plm);
}
// 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 initialised = 0;
if (initialised)
return 0;
initialised = 1;
// Shorten the code that declares a lexer and ensures it is linked in by calling a method.
#define LINK_LEXER(lexer) extern LexerModule lexer; Catalogue::AddLexerModule(&lexer);
//++Autogenerated -- run scripts/LexGen.py to regenerate
//**\(\tLINK_LEXER(\*);\n\)
LINK_LEXER(lmA68k);
LINK_LEXER(lmAbaqus);
LINK_LEXER(lmAda);
LINK_LEXER(lmAPDL);
LINK_LEXER(lmAs);
LINK_LEXER(lmAsm);
LINK_LEXER(lmAsn1);
LINK_LEXER(lmASY);
LINK_LEXER(lmAU3);
LINK_LEXER(lmAVE);
LINK_LEXER(lmAVS);
LINK_LEXER(lmBaan);
LINK_LEXER(lmBash);
LINK_LEXER(lmBatch);
LINK_LEXER(lmBibTeX);
LINK_LEXER(lmBlitzBasic);
LINK_LEXER(lmBullant);
LINK_LEXER(lmCaml);
LINK_LEXER(lmClw);
LINK_LEXER(lmClwNoCase);
LINK_LEXER(lmCmake);
LINK_LEXER(lmCOBOL);
LINK_LEXER(lmCoffeeScript);
LINK_LEXER(lmConf);
LINK_LEXER(lmCPP);
LINK_LEXER(lmCPPNoCase);
LINK_LEXER(lmCsound);
LINK_LEXER(lmCss);
LINK_LEXER(lmD);
LINK_LEXER(lmDiff);
LINK_LEXER(lmDMAP);
LINK_LEXER(lmDMIS);
LINK_LEXER(lmECL);
LINK_LEXER(lmEDIFACT);
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(lmGAP);
LINK_LEXER(lmGui4Cli);
LINK_LEXER(lmHaskell);
LINK_LEXER(lmHTML);
LINK_LEXER(lmIHex);
LINK_LEXER(lmIndent);
LINK_LEXER(lmInno);
LINK_LEXER(lmJSON);
LINK_LEXER(lmKix);
LINK_LEXER(lmKVIrc);
LINK_LEXER(lmLatex);
LINK_LEXER(lmLISP);
LINK_LEXER(lmLiterateHaskell);
LINK_LEXER(lmLot);
LINK_LEXER(lmLout);
#if !defined(SCINTILLA_QT)
LINK_LEXER(lmLPeg);
#endif
LINK_LEXER(lmLua);
LINK_LEXER(lmMagikSF);
LINK_LEXER(lmMake);
LINK_LEXER(lmMarkdown);
LINK_LEXER(lmMatlab);
LINK_LEXER(lmMaxima);
LINK_LEXER(lmMETAPOST);
LINK_LEXER(lmMMIXAL);
LINK_LEXER(lmModula);
LINK_LEXER(lmMSSQL);
LINK_LEXER(lmMySQL);
LINK_LEXER(lmNimrod);
LINK_LEXER(lmNncrontab);
LINK_LEXER(lmNsis);
LINK_LEXER(lmNull);
LINK_LEXER(lmOctave);
LINK_LEXER(lmOpal);
LINK_LEXER(lmOScript);
LINK_LEXER(lmPascal);
LINK_LEXER(lmPB);
LINK_LEXER(lmPerl);
LINK_LEXER(lmPHPSCRIPT);
LINK_LEXER(lmPLM);
LINK_LEXER(lmPO);
LINK_LEXER(lmPOV);
LINK_LEXER(lmPowerPro);
LINK_LEXER(lmPowerShell);
LINK_LEXER(lmProgress);
LINK_LEXER(lmProps);
LINK_LEXER(lmPS);
LINK_LEXER(lmPureBasic);
LINK_LEXER(lmPython);
LINK_LEXER(lmR);
LINK_LEXER(lmREBOL);
LINK_LEXER(lmRegistry);
LINK_LEXER(lmRuby);
LINK_LEXER(lmRust);
LINK_LEXER(lmSAS);
LINK_LEXER(lmScriptol);
LINK_LEXER(lmSmalltalk);
LINK_LEXER(lmSML);
LINK_LEXER(lmSorc);
LINK_LEXER(lmSpecman);
LINK_LEXER(lmSpice);
LINK_LEXER(lmSQL);
LINK_LEXER(lmSrec);
LINK_LEXER(lmStata);
LINK_LEXER(lmSTTXT);
LINK_LEXER(lmTACL);
LINK_LEXER(lmTADS3);
LINK_LEXER(lmTAL);
LINK_LEXER(lmTCL);
LINK_LEXER(lmTCMD);
LINK_LEXER(lmTEHex);
LINK_LEXER(lmTeX);
LINK_LEXER(lmTxt2tags);
LINK_LEXER(lmVB);
LINK_LEXER(lmVBScript);
LINK_LEXER(lmVerilog);
LINK_LEXER(lmVHDL);
LINK_LEXER(lmVisualProlog);
LINK_LEXER(lmXML);
LINK_LEXER(lmYAML);
//--Autogenerated -- end of automatically generated section
return 1;
}

View File

@@ -0,0 +1,24 @@
// Scintilla source code edit control
/** @file Catalogue.h
** Lexer infrastructure.
** Contains a list of LexerModules which can be searched to find a module appropriate for a
** particular language.
**/
// Copyright 1998-2010 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef CATALOGUE_H
#define CATALOGUE_H
namespace Scintilla {
class Catalogue {
public:
static const LexerModule *Find(int language);
static const LexerModule *Find(const char *languageName);
static void AddLexerModule(LexerModule *plm);
};
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,215 @@
// 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
namespace Scintilla {
// Interface to per-line data that wants to see each line insertion and deletion
class PerLine {
public:
virtual ~PerLine() {}
virtual void Init()=0;
virtual void InsertLine(Sci::Line line)=0;
virtual void RemoveLine(Sci::Line line)=0;
};
/**
* The line vector contains information about each of the lines in a cell buffer.
*/
class ILineVector;
enum actionType { insertAction, removeAction, startAction, containerAction };
/**
* Actions are used to store all the information required to perform one undo/redo step.
*/
class Action {
public:
actionType at;
Sci::Position position;
std::unique_ptr<char[]> data;
Sci::Position lenData;
bool mayCoalesce;
Action();
// Deleted so Action objects can not be copied.
Action(const Action &other) = delete;
Action &operator=(const Action &other) = delete;
Action &operator=(const Action &&other) = delete;
// Move constructor allows vector to be resized without reallocating.
Action(Action &&other) noexcept = default;
~Action();
void Create(actionType at_, Sci::Position position_=0, const char *data_=nullptr, Sci::Position lenData_=0, bool mayCoalesce_=true);
void Clear();
};
/**
*
*/
class UndoHistory {
std::vector<Action> actions;
int maxAction;
int currentAction;
int undoSequenceDepth;
int savePoint;
int tentativePoint;
void EnsureUndoRoom();
public:
UndoHistory();
// Deleted so UndoHistory objects can not be copied.
UndoHistory(const UndoHistory &) = delete;
UndoHistory(UndoHistory &&) = delete;
void operator=(const UndoHistory &) = delete;
void operator=(UndoHistory &&) = delete;
~UndoHistory();
const char *AppendAction(actionType at, Sci::Position position, const char *data, Sci::Position lengthData, bool &startSequence, bool mayCoalesce=true);
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;
// Tentative actions are used for input composition so that it can be undone cleanly
void TentativeStart();
void TentativeCommit();
bool TentativeActive() const noexcept { return tentativePoint >= 0; }
int TentativeSteps();
/// 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:
bool hasStyles;
bool largeDocument;
SplitVector<char> substance;
SplitVector<char> style;
bool readOnly;
bool utf8Substance;
int utf8LineEnds;
bool collectingUndo;
UndoHistory uh;
std::unique_ptr<ILineVector> plv;
bool UTF8LineEndOverlaps(Sci::Position position) const;
bool UTF8IsCharacterBoundary(Sci::Position position) const;
void ResetLineEnds();
void RecalculateIndexLineStarts(Sci::Line lineFirst, Sci::Line lineLast);
bool MaintainingLineCharacterIndex() const noexcept;
/// Actions without undo
void BasicInsertString(Sci::Position position, const char *s, Sci::Position insertLength);
void BasicDeleteChars(Sci::Position position, Sci::Position deleteLength);
public:
CellBuffer(bool hasStyles_, bool largeDocument_);
// Deleted so CellBuffer objects can not be copied.
CellBuffer(const CellBuffer &) = delete;
CellBuffer(CellBuffer &&) = delete;
void operator=(const CellBuffer &) = delete;
void operator=(CellBuffer &&) = delete;
~CellBuffer();
/// Retrieving positions outside the range of the buffer works and returns 0
char CharAt(Sci::Position position) const noexcept;
unsigned char UCharAt(Sci::Position position) const noexcept;
void GetCharRange(char *buffer, Sci::Position position, Sci::Position lengthRetrieve) const;
char StyleAt(Sci::Position position) const noexcept;
void GetStyleRange(unsigned char *buffer, Sci::Position position, Sci::Position lengthRetrieve) const;
const char *BufferPointer();
const char *RangePointer(Sci::Position position, Sci::Position rangeLength);
Sci::Position GapPosition() const;
Sci::Position Length() const noexcept;
void Allocate(Sci::Position newSize);
void SetUTF8Substance(bool utf8Substance_);
int GetLineEndTypes() const { return utf8LineEnds; }
void SetLineEndTypes(int utf8LineEnds_);
bool ContainsLineEnd(const char *s, Sci::Position length) const;
void SetPerLine(PerLine *pl);
int LineCharacterIndex() const noexcept;
void AllocateLineCharacterIndex(int lineCharacterIndex);
void ReleaseLineCharacterIndex(int lineCharacterIndex);
Sci::Line Lines() const noexcept;
Sci::Position LineStart(Sci::Line line) const noexcept;
Sci::Position IndexLineStart(Sci::Line line, int lineCharacterIndex) const noexcept;
Sci::Line LineFromPosition(Sci::Position pos) const noexcept;
Sci::Line LineFromPositionIndex(Sci::Position pos, int lineCharacterIndex) const noexcept;
void InsertLine(Sci::Line line, Sci::Position position, bool lineStart);
void RemoveLine(Sci::Line line);
const char *InsertString(Sci::Position position, const char *s, Sci::Position insertLength, bool &startSequence);
/// 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(Sci::Position position, char styleValue);
bool SetStyleFor(Sci::Position position, Sci::Position lengthStyle, char styleValue);
const char *DeleteChars(Sci::Position position, Sci::Position deleteLength, bool &startSequence);
bool IsReadOnly() const;
void SetReadOnly(bool set);
bool IsLarge() const;
bool HasStyles() const;
/// 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;
void TentativeStart();
void TentativeCommit();
bool TentativeActive() const;
int TentativeSteps();
bool SetUndoCollection(bool collectUndo);
bool IsCollectingUndo() const;
void BeginUndoAction();
void EndUndoAction();
void AddUndoAction(Sci::Position token, bool mayCoalesce);
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() const;
int StartUndo();
const Action &GetUndoStep() const;
void PerformUndoStep();
bool CanRedo() const;
int StartRedo();
const Action &GetRedoStep() const;
void PerformRedoStep();
};
}
#endif

View File

@@ -0,0 +1,59 @@
// 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 <cstdlib>
#include <cctype>
#include <stdexcept>
#include "CharClassify.h"
using namespace Scintilla;
CharClassify::CharClassify() : charClass{} {
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++;
}
}
}
int CharClassify::GetCharsOfClass(cc characterClass, unsigned char *buffer) const {
// Get characters belonging to the given char class; return the number
// of characters (if the buffer is NULL, don't write to it).
int count = 0;
for (int ch = maxChar - 1; ch >= 0; --ch) {
if (charClass[ch] == characterClass) {
++count;
if (buffer) {
*buffer = static_cast<unsigned char>(ch);
buffer++;
}
}
}
return count;
}

View File

@@ -0,0 +1,31 @@
// Scintilla source code edit control
/** @file CharClassify.h
** Character classifications used by Document and RESearch.
**/
// Copyright 2006-2009 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
namespace Scintilla {
class CharClassify {
public:
CharClassify();
enum cc { ccSpace, ccNewLine, ccWord, ccPunctuation };
void SetDefaultCharClasses(bool includeWordClass);
void SetCharClasses(const unsigned char *chars, cc newCharClass);
int GetCharsOfClass(cc characterClass, unsigned char *buffer) const;
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

View File

@@ -0,0 +1,418 @@
// Scintilla source code edit control
/** @file ContractionState.cxx
** Manages visibility of lines for folding and wrapping.
**/
// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstddef>
#include <cassert>
#include <cstring>
#include <stdexcept>
#include <vector>
#include <algorithm>
#include <memory>
#include "Platform.h"
#include "Position.h"
#include "UniqueString.h"
#include "SplitVector.h"
#include "Partitioning.h"
#include "RunStyles.h"
#include "SparseVector.h"
#include "ContractionState.h"
using namespace Scintilla;
namespace {
template <typename LINE>
class ContractionState final : public IContractionState {
// These contain 1 element for every document line.
std::unique_ptr<RunStyles<LINE, char>> visible;
std::unique_ptr<RunStyles<LINE, char>> expanded;
std::unique_ptr<RunStyles<LINE, int>> heights;
std::unique_ptr<SparseVector<UniqueString>> foldDisplayTexts;
std::unique_ptr<Partitioning<LINE>> displayLines;
LINE linesInDocument;
void EnsureData();
bool OneToOne() const noexcept {
// True when each document line is exactly one display line so need for
// complex data structures.
return visible == nullptr;
}
void InsertLine(Sci::Line lineDoc);
void DeleteLine(Sci::Line lineDoc);
public:
ContractionState() noexcept;
// Deleted so ContractionState objects can not be copied.
ContractionState(const ContractionState &) = delete;
void operator=(const ContractionState &) = delete;
ContractionState(ContractionState &&) = delete;
void operator=(ContractionState &&) = delete;
~ContractionState() override;
void Clear() noexcept override;
Sci::Line LinesInDoc() const override;
Sci::Line LinesDisplayed() const override;
Sci::Line DisplayFromDoc(Sci::Line lineDoc) const override;
Sci::Line DisplayLastFromDoc(Sci::Line lineDoc) const override;
Sci::Line DocFromDisplay(Sci::Line lineDisplay) const override;
void InsertLines(Sci::Line lineDoc, Sci::Line lineCount) override;
void DeleteLines(Sci::Line lineDoc, Sci::Line lineCount) override;
bool GetVisible(Sci::Line lineDoc) const override;
bool SetVisible(Sci::Line lineDocStart, Sci::Line lineDocEnd, bool isVisible) override;
bool HiddenLines() const override;
const char *GetFoldDisplayText(Sci::Line lineDoc) const override;
bool GetFoldDisplayTextShown(Sci::Line lineDoc) const override;
bool SetFoldDisplayText(Sci::Line lineDoc, const char *text) override;
bool GetExpanded(Sci::Line lineDoc) const override;
bool SetExpanded(Sci::Line lineDoc, bool isExpanded) override;
Sci::Line ContractedNext(Sci::Line lineDocStart) const override;
int GetHeight(Sci::Line lineDoc) const override;
bool SetHeight(Sci::Line lineDoc, int height) override;
void ShowAll() override;
void Check() const;
};
template <typename LINE>
ContractionState<LINE>::ContractionState() noexcept : linesInDocument(1) {
}
template <typename LINE>
ContractionState<LINE>::~ContractionState() {
Clear();
}
template <typename LINE>
void ContractionState<LINE>::EnsureData() {
if (OneToOne()) {
visible = std::unique_ptr<RunStyles<LINE, char>>(new RunStyles<LINE, char>());
expanded = std::unique_ptr<RunStyles<LINE, char>>(new RunStyles<LINE, char>());
heights = std::unique_ptr<RunStyles<LINE, int>>(new RunStyles<LINE, int>());
foldDisplayTexts = std::unique_ptr<SparseVector<UniqueString>>(new SparseVector<UniqueString>());
displayLines = std::unique_ptr<Partitioning<LINE>>(new Partitioning<LINE>(4));
InsertLines(0, linesInDocument);
}
}
template <typename LINE>
void ContractionState<LINE>::InsertLine(Sci::Line lineDoc) {
if (OneToOne()) {
linesInDocument++;
} else {
const LINE lineDocCast = static_cast<LINE>(lineDoc);
visible->InsertSpace(lineDocCast, 1);
visible->SetValueAt(lineDocCast, 1);
expanded->InsertSpace(lineDocCast, 1);
expanded->SetValueAt(lineDocCast, 1);
heights->InsertSpace(lineDocCast, 1);
heights->SetValueAt(lineDocCast, 1);
foldDisplayTexts->InsertSpace(lineDocCast, 1);
foldDisplayTexts->SetValueAt(lineDocCast, nullptr);
const Sci::Line lineDisplay = DisplayFromDoc(lineDoc);
displayLines->InsertPartition(lineDocCast, static_cast<LINE>(lineDisplay));
displayLines->InsertText(lineDocCast, 1);
}
}
template <typename LINE>
void ContractionState<LINE>::DeleteLine(Sci::Line lineDoc) {
if (OneToOne()) {
linesInDocument--;
} else {
const LINE lineDocCast = static_cast<LINE>(lineDoc);
if (GetVisible(lineDoc)) {
displayLines->InsertText(lineDocCast, -heights->ValueAt(lineDocCast));
}
displayLines->RemovePartition(lineDocCast);
visible->DeleteRange(lineDocCast, 1);
expanded->DeleteRange(lineDocCast, 1);
heights->DeleteRange(lineDocCast, 1);
foldDisplayTexts->DeletePosition(lineDocCast);
}
}
template <typename LINE>
void ContractionState<LINE>::Clear() noexcept {
visible.reset();
expanded.reset();
heights.reset();
foldDisplayTexts.reset();
displayLines.reset();
linesInDocument = 1;
}
template <typename LINE>
Sci::Line ContractionState<LINE>::LinesInDoc() const {
if (OneToOne()) {
return linesInDocument;
} else {
return displayLines->Partitions() - 1;
}
}
template <typename LINE>
Sci::Line ContractionState<LINE>::LinesDisplayed() const {
if (OneToOne()) {
return linesInDocument;
} else {
return displayLines->PositionFromPartition(static_cast<LINE>(LinesInDoc()));
}
}
template <typename LINE>
Sci::Line ContractionState<LINE>::DisplayFromDoc(Sci::Line lineDoc) const {
if (OneToOne()) {
return (lineDoc <= linesInDocument) ? lineDoc : linesInDocument;
} else {
if (lineDoc > displayLines->Partitions())
lineDoc = displayLines->Partitions();
return displayLines->PositionFromPartition(static_cast<LINE>(lineDoc));
}
}
template <typename LINE>
Sci::Line ContractionState<LINE>::DisplayLastFromDoc(Sci::Line lineDoc) const {
return DisplayFromDoc(lineDoc) + GetHeight(lineDoc) - 1;
}
template <typename LINE>
Sci::Line ContractionState<LINE>::DocFromDisplay(Sci::Line lineDisplay) const {
if (OneToOne()) {
return lineDisplay;
} else {
if (lineDisplay <= 0) {
return 0;
}
if (lineDisplay > LinesDisplayed()) {
return displayLines->PartitionFromPosition(static_cast<LINE>(LinesDisplayed()));
}
const Sci::Line lineDoc = displayLines->PartitionFromPosition(static_cast<LINE>(lineDisplay));
PLATFORM_ASSERT(GetVisible(lineDoc));
return lineDoc;
}
}
template <typename LINE>
void ContractionState<LINE>::InsertLines(Sci::Line lineDoc, Sci::Line lineCount) {
for (int l = 0; l < lineCount; l++) {
InsertLine(lineDoc + l);
}
Check();
}
template <typename LINE>
void ContractionState<LINE>::DeleteLines(Sci::Line lineDoc, Sci::Line lineCount) {
for (Sci::Line l = 0; l < lineCount; l++) {
DeleteLine(lineDoc);
}
Check();
}
template <typename LINE>
bool ContractionState<LINE>::GetVisible(Sci::Line lineDoc) const {
if (OneToOne()) {
return true;
} else {
if (lineDoc >= visible->Length())
return true;
return visible->ValueAt(static_cast<LINE>(lineDoc)) == 1;
}
}
template <typename LINE>
bool ContractionState<LINE>::SetVisible(Sci::Line lineDocStart, Sci::Line lineDocEnd, bool isVisible) {
if (OneToOne() && isVisible) {
return false;
} else {
EnsureData();
Sci::Line delta = 0;
Check();
if ((lineDocStart <= lineDocEnd) && (lineDocStart >= 0) && (lineDocEnd < LinesInDoc())) {
for (Sci::Line line = lineDocStart; line <= lineDocEnd; line++) {
if (GetVisible(line) != isVisible) {
const int heightLine = heights->ValueAt(static_cast<LINE>(line));
const int difference = isVisible ? heightLine : -heightLine;
visible->SetValueAt(static_cast<LINE>(line), isVisible ? 1 : 0);
displayLines->InsertText(static_cast<LINE>(line), difference);
delta += difference;
}
}
} else {
return false;
}
Check();
return delta != 0;
}
}
template <typename LINE>
bool ContractionState<LINE>::HiddenLines() const {
if (OneToOne()) {
return false;
} else {
return !visible->AllSameAs(1);
}
}
template <typename LINE>
const char *ContractionState<LINE>::GetFoldDisplayText(Sci::Line lineDoc) const {
Check();
return foldDisplayTexts->ValueAt(lineDoc).get();
}
template <typename LINE>
bool ContractionState<LINE>::GetFoldDisplayTextShown(Sci::Line lineDoc) const {
return !GetExpanded(lineDoc) && GetFoldDisplayText(lineDoc);
}
template <typename LINE>
bool ContractionState<LINE>::SetFoldDisplayText(Sci::Line lineDoc, const char *text) {
EnsureData();
const char *foldText = foldDisplayTexts->ValueAt(lineDoc).get();
if (!foldText || !text || 0 != strcmp(text, foldText)) {
UniqueString uns = UniqueStringCopy(text);
foldDisplayTexts->SetValueAt(lineDoc, std::move(uns));
Check();
return true;
} else {
Check();
return false;
}
}
template <typename LINE>
bool ContractionState<LINE>::GetExpanded(Sci::Line lineDoc) const {
if (OneToOne()) {
return true;
} else {
Check();
return expanded->ValueAt(static_cast<LINE>(lineDoc)) == 1;
}
}
template <typename LINE>
bool ContractionState<LINE>::SetExpanded(Sci::Line lineDoc, bool isExpanded) {
if (OneToOne() && isExpanded) {
return false;
} else {
EnsureData();
if (isExpanded != (expanded->ValueAt(static_cast<LINE>(lineDoc)) == 1)) {
expanded->SetValueAt(static_cast<LINE>(lineDoc), isExpanded ? 1 : 0);
Check();
return true;
} else {
Check();
return false;
}
}
}
template <typename LINE>
Sci::Line ContractionState<LINE>::ContractedNext(Sci::Line lineDocStart) const {
if (OneToOne()) {
return -1;
} else {
Check();
if (!expanded->ValueAt(static_cast<LINE>(lineDocStart))) {
return lineDocStart;
} else {
const Sci::Line lineDocNextChange = expanded->EndRun(static_cast<LINE>(lineDocStart));
if (lineDocNextChange < LinesInDoc())
return lineDocNextChange;
else
return -1;
}
}
}
template <typename LINE>
int ContractionState<LINE>::GetHeight(Sci::Line lineDoc) const {
if (OneToOne()) {
return 1;
} else {
return heights->ValueAt(static_cast<LINE>(lineDoc));
}
}
// Set the number of display lines needed for this line.
// Return true if this is a change.
template <typename LINE>
bool ContractionState<LINE>::SetHeight(Sci::Line lineDoc, int height) {
if (OneToOne() && (height == 1)) {
return false;
} else if (lineDoc < LinesInDoc()) {
EnsureData();
if (GetHeight(lineDoc) != height) {
if (GetVisible(lineDoc)) {
displayLines->InsertText(static_cast<LINE>(lineDoc), height - GetHeight(lineDoc));
}
heights->SetValueAt(static_cast<LINE>(lineDoc), height);
Check();
return true;
} else {
Check();
return false;
}
} else {
return false;
}
}
template <typename LINE>
void ContractionState<LINE>::ShowAll() {
const LINE lines = static_cast<LINE>(LinesInDoc());
Clear();
linesInDocument = lines;
}
// Debugging checks
template <typename LINE>
void ContractionState<LINE>::Check() const {
#ifdef CHECK_CORRECTNESS
for (Sci::Line vline = 0; vline < LinesDisplayed(); vline++) {
const Sci::Line lineDoc = DocFromDisplay(vline);
PLATFORM_ASSERT(GetVisible(lineDoc));
}
for (Sci::Line lineDoc = 0; lineDoc < LinesInDoc(); lineDoc++) {
const Sci::Line displayThis = DisplayFromDoc(lineDoc);
const Sci::Line displayNext = DisplayFromDoc(lineDoc + 1);
const Sci::Line height = displayNext - displayThis;
PLATFORM_ASSERT(height >= 0);
if (GetVisible(lineDoc)) {
PLATFORM_ASSERT(GetHeight(lineDoc) == height);
} else {
PLATFORM_ASSERT(0 == height);
}
}
#endif
}
}
namespace Scintilla {
std::unique_ptr<IContractionState> ContractionStateCreate(bool largeDocument) {
if (largeDocument)
return std::unique_ptr<ContractionState<Sci::Line>>(new ContractionState<Sci::Line>());
else
return std::unique_ptr<ContractionState<int>>(new ContractionState<int>());
}
}

View File

@@ -0,0 +1,52 @@
// Scintilla source code edit control
/** @file ContractionState.h
** Manages visibility of lines for folding and wrapping.
**/
// Copyright 1998-2007 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
namespace Scintilla {
/**
*/
class IContractionState {
public:
virtual ~IContractionState() {};
virtual void Clear()=0;
virtual Sci::Line LinesInDoc() const=0;
virtual Sci::Line LinesDisplayed() const=0;
virtual Sci::Line DisplayFromDoc(Sci::Line lineDoc) const=0;
virtual Sci::Line DisplayLastFromDoc(Sci::Line lineDoc) const=0;
virtual Sci::Line DocFromDisplay(Sci::Line lineDisplay) const=0;
virtual void InsertLines(Sci::Line lineDoc, Sci::Line lineCount)=0;
virtual void DeleteLines(Sci::Line lineDoc, Sci::Line lineCount)=0;
virtual bool GetVisible(Sci::Line lineDoc) const=0;
virtual bool SetVisible(Sci::Line lineDocStart, Sci::Line lineDocEnd, bool isVisible)=0;
virtual bool HiddenLines() const=0;
virtual const char *GetFoldDisplayText(Sci::Line lineDoc) const=0;
virtual bool GetFoldDisplayTextShown(Sci::Line lineDoc) const=0;
virtual bool SetFoldDisplayText(Sci::Line lineDoc, const char *text)=0;
virtual bool GetExpanded(Sci::Line lineDoc) const=0;
virtual bool SetExpanded(Sci::Line lineDoc, bool isExpanded)=0;
virtual Sci::Line ContractedNext(Sci::Line lineDocStart) const=0;
virtual int GetHeight(Sci::Line lineDoc) const=0;
virtual bool SetHeight(Sci::Line lineDoc, int height)=0;
virtual void ShowAll()=0;
};
std::unique_ptr<IContractionState> ContractionStateCreate(bool largeDocument);
}
#endif

View File

@@ -0,0 +1,42 @@
// Scintilla source code edit control
/** @file DBCS.cxx
** Functions to handle DBCS double byte encodings like Shift-JIS.
**/
// Copyright 2017 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include "DBCS.h"
using namespace Scintilla;
namespace Scintilla {
bool DBCSIsLeadByte(int codePage, char ch) noexcept {
// Byte ranges found in Wikipedia articles with relevant search strings in each case
const unsigned char uch = ch;
switch (codePage) {
case 932:
// Shift_jis
return ((uch >= 0x81) && (uch <= 0x9F)) ||
((uch >= 0xE0) && (uch <= 0xFC));
// Lead bytes F0 to FC may be a Microsoft addition.
case 936:
// GBK
return (uch >= 0x81) && (uch <= 0xFE);
case 949:
// Korean Wansung KS C-5601-1987
return (uch >= 0x81) && (uch <= 0xFE);
case 950:
// Big5
return (uch >= 0x81) && (uch <= 0xFE);
case 1361:
// Korean Johab KS C-5601-1992
return
((uch >= 0x84) && (uch <= 0xD3)) ||
((uch >= 0xD8) && (uch <= 0xDE)) ||
((uch >= 0xE0) && (uch <= 0xF9));
}
return false;
}
}

View File

@@ -0,0 +1,17 @@
// Scintilla source code edit control
/** @file DBCS.h
** Functions to handle DBCS double byte encodings like Shift-JIS.
**/
// Copyright 2017 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef DBCS_H
#define DBCS_H
namespace Scintilla {
bool DBCSIsLeadByte(int codePage, char ch) noexcept;
}
#endif

View File

@@ -0,0 +1,316 @@
/** @file Decoration.cxx
** Visual elements added over text.
**/
// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cstdarg>
#include <stdexcept>
#include <vector>
#include <algorithm>
#include <memory>
#include "Platform.h"
#include "Scintilla.h"
#include "Position.h"
#include "SplitVector.h"
#include "Partitioning.h"
#include "RunStyles.h"
#include "Decoration.h"
using namespace Scintilla;
namespace {
template <typename POS>
class Decoration : public IDecoration {
int indicator;
public:
RunStyles<POS, int> rs;
explicit Decoration(int indicator_) : indicator(indicator_) {
}
~Decoration() override {
}
bool Empty() const override {
return (rs.Runs() == 1) && (rs.AllSameAs(0));
}
int Indicator() const override {
return indicator;
}
Sci::Position Length() const override {
return rs.Length();
}
int ValueAt(Sci::Position position) const override {
return rs.ValueAt(static_cast<POS>(position));
}
Sci::Position StartRun(Sci::Position position) const override {
return rs.StartRun(static_cast<POS>(position));
}
Sci::Position EndRun(Sci::Position position) const override {
return rs.EndRun(static_cast<POS>(position));
}
void SetValueAt(Sci::Position position, int value) override {
rs.SetValueAt(static_cast<POS>(position), value);
}
void InsertSpace(Sci::Position position, Sci::Position insertLength) override {
rs.InsertSpace(static_cast<POS>(position), static_cast<POS>(insertLength));
}
Sci::Position Runs() const override {
return rs.Runs();
}
};
template <typename POS>
class DecorationList : public IDecorationList {
int currentIndicator;
int currentValue;
Decoration<POS> *current; // Cached so FillRange doesn't have to search for each call.
Sci::Position lengthDocument;
// Ordered by indicator
std::vector<std::unique_ptr<Decoration<POS>>> decorationList;
std::vector<const IDecoration*> decorationView; // Read-only view of decorationList
bool clickNotified;
Decoration<POS> *DecorationFromIndicator(int indicator);
Decoration<POS> *Create(int indicator, Sci::Position length);
void Delete(int indicator);
void DeleteAnyEmpty();
void SetView();
public:
DecorationList();
~DecorationList() override;
const std::vector<const IDecoration*> &View() const override {
return decorationView;
}
void SetCurrentIndicator(int indicator) override;
int GetCurrentIndicator() const override { return currentIndicator; }
void SetCurrentValue(int value) override;
int GetCurrentValue() const override { return currentValue; }
// Returns changed=true if some values may have changed
FillResult<Sci::Position> FillRange(Sci::Position position, int value, Sci::Position fillLength) override;
void InsertSpace(Sci::Position position, Sci::Position insertLength) override;
void DeleteRange(Sci::Position position, Sci::Position deleteLength) override;
void DeleteLexerDecorations() override;
int AllOnFor(Sci::Position position) const override;
int ValueAt(int indicator, Sci::Position position) override;
Sci::Position Start(int indicator, Sci::Position position) override;
Sci::Position End(int indicator, Sci::Position position) override;
bool ClickNotified() const override {
return clickNotified;
}
void SetClickNotified(bool notified) override {
clickNotified = notified;
}
};
template <typename POS>
DecorationList<POS>::DecorationList() : currentIndicator(0), currentValue(1), current(nullptr),
lengthDocument(0), clickNotified(false) {
}
template <typename POS>
DecorationList<POS>::~DecorationList() {
current = nullptr;
}
template <typename POS>
Decoration<POS> *DecorationList<POS>::DecorationFromIndicator(int indicator) {
for (const std::unique_ptr<Decoration<POS>> &deco : decorationList) {
if (deco->Indicator() == indicator) {
return deco.get();
}
}
return nullptr;
}
template <typename POS>
Decoration<POS> *DecorationList<POS>::Create(int indicator, Sci::Position length) {
currentIndicator = indicator;
std::unique_ptr<Decoration<POS>> decoNew = std::unique_ptr<Decoration<POS>>(new Decoration<POS>(indicator));
decoNew->rs.InsertSpace(0, static_cast<POS>(length));
typename std::vector<std::unique_ptr<Decoration<POS>>>::iterator it = std::lower_bound(
decorationList.begin(), decorationList.end(), decoNew,
[](const std::unique_ptr<Decoration<POS>> &a, const std::unique_ptr<Decoration<POS>> &b) {
return a->Indicator() < b->Indicator();
});
typename std::vector<std::unique_ptr<Decoration<POS>>>::iterator itAdded =
decorationList.insert(it, std::move(decoNew));
SetView();
return itAdded->get();
}
template <typename POS>
void DecorationList<POS>::Delete(int indicator) {
decorationList.erase(std::remove_if(decorationList.begin(), decorationList.end(),
[indicator](const std::unique_ptr<Decoration<POS>> &deco) {
return deco->Indicator() == indicator;
}), decorationList.end());
current = nullptr;
SetView();
}
template <typename POS>
void DecorationList<POS>::SetCurrentIndicator(int indicator) {
currentIndicator = indicator;
current = DecorationFromIndicator(indicator);
currentValue = 1;
}
template <typename POS>
void DecorationList<POS>::SetCurrentValue(int value) {
currentValue = value ? value : 1;
}
template <typename POS>
FillResult<Sci::Position> DecorationList<POS>::FillRange(Sci::Position position, int value, Sci::Position fillLength) {
if (!current) {
current = DecorationFromIndicator(currentIndicator);
if (!current) {
current = Create(currentIndicator, lengthDocument);
}
}
// Converting result from POS to Sci::Position as callers not polymorphic.
const FillResult<POS> frInPOS = current->rs.FillRange(static_cast<POS>(position), value, static_cast<POS>(fillLength));
const FillResult<Sci::Position> fr { frInPOS.changed, frInPOS.position, frInPOS.fillLength };
if (current->Empty()) {
Delete(currentIndicator);
}
return fr;
}
template <typename POS>
void DecorationList<POS>::InsertSpace(Sci::Position position, Sci::Position insertLength) {
const bool atEnd = position == lengthDocument;
lengthDocument += insertLength;
for (const std::unique_ptr<Decoration<POS>> &deco : decorationList) {
deco->rs.InsertSpace(static_cast<POS>(position), static_cast<POS>(insertLength));
if (atEnd) {
deco->rs.FillRange(static_cast<POS>(position), 0, static_cast<POS>(insertLength));
}
}
}
template <typename POS>
void DecorationList<POS>::DeleteRange(Sci::Position position, Sci::Position deleteLength) {
lengthDocument -= deleteLength;
for (const std::unique_ptr<Decoration<POS>> &deco : decorationList) {
deco->rs.DeleteRange(static_cast<POS>(position), static_cast<POS>(deleteLength));
}
DeleteAnyEmpty();
if (decorationList.size() != decorationView.size()) {
// One or more empty decorations deleted so update view.
current = nullptr;
SetView();
}
}
template <typename POS>
void DecorationList<POS>::DeleteLexerDecorations() {
decorationList.erase(std::remove_if(decorationList.begin(), decorationList.end(),
[](const std::unique_ptr<Decoration<POS>> &deco) {
return deco->Indicator() < INDIC_CONTAINER;
}), decorationList.end());
current = nullptr;
SetView();
}
template <typename POS>
void DecorationList<POS>::DeleteAnyEmpty() {
if (lengthDocument == 0) {
decorationList.clear();
} else {
decorationList.erase(std::remove_if(decorationList.begin(), decorationList.end(),
[](const std::unique_ptr<Decoration<POS>> &deco) {
return deco->Empty();
}), decorationList.end());
}
}
template <typename POS>
void DecorationList<POS>::SetView() {
decorationView.clear();
for (const std::unique_ptr<Decoration<POS>> &deco : decorationList) {
decorationView.push_back(deco.get());
}
}
template <typename POS>
int DecorationList<POS>::AllOnFor(Sci::Position position) const {
int mask = 0;
for (const std::unique_ptr<Decoration<POS>> &deco : decorationList) {
if (deco->rs.ValueAt(static_cast<POS>(position))) {
if (deco->Indicator() < INDIC_IME) {
mask |= 1 << deco->Indicator();
}
}
}
return mask;
}
template <typename POS>
int DecorationList<POS>::ValueAt(int indicator, Sci::Position position) {
const Decoration<POS> *deco = DecorationFromIndicator(indicator);
if (deco) {
return deco->rs.ValueAt(static_cast<POS>(position));
}
return 0;
}
template <typename POS>
Sci::Position DecorationList<POS>::Start(int indicator, Sci::Position position) {
const Decoration<POS> *deco = DecorationFromIndicator(indicator);
if (deco) {
return deco->rs.StartRun(static_cast<POS>(position));
}
return 0;
}
template <typename POS>
Sci::Position DecorationList<POS>::End(int indicator, Sci::Position position) {
const Decoration<POS> *deco = DecorationFromIndicator(indicator);
if (deco) {
return deco->rs.EndRun(static_cast<POS>(position));
}
return 0;
}
}
namespace Scintilla {
std::unique_ptr<IDecoration> DecorationCreate(bool largeDocument, int indicator) {
if (largeDocument)
return std::unique_ptr<Decoration<Sci::Position>>(new Decoration<Sci::Position>(indicator));
else
return std::unique_ptr<Decoration<int>>(new Decoration<int>(indicator));
}
std::unique_ptr<IDecorationList> DecorationListCreate(bool largeDocument) {
if (largeDocument)
return std::unique_ptr<DecorationList<Sci::Position>>(new DecorationList<Sci::Position>());
else
return std::unique_ptr<DecorationList<int>>(new DecorationList<int>());
}
}

View File

@@ -0,0 +1,59 @@
/** @file Decoration.h
** Visual elements added over text.
**/
// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef DECORATION_H
#define DECORATION_H
namespace Scintilla {
class IDecoration {
public:
virtual ~IDecoration() {}
virtual bool Empty() const = 0;
virtual int Indicator() const = 0;
virtual Sci::Position Length() const = 0;
virtual int ValueAt(Sci::Position position) const = 0;
virtual Sci::Position StartRun(Sci::Position position) const = 0;
virtual Sci::Position EndRun(Sci::Position position) const = 0;
virtual void SetValueAt(Sci::Position position, int value) = 0;
virtual void InsertSpace(Sci::Position position, Sci::Position insertLength) = 0;
virtual Sci::Position Runs() const = 0;
};
class IDecorationList {
public:
virtual ~IDecorationList() {}
virtual const std::vector<const IDecoration*> &View() const =0;
virtual void SetCurrentIndicator(int indicator) = 0;
virtual int GetCurrentIndicator() const = 0;
virtual void SetCurrentValue(int value) = 0;
virtual int GetCurrentValue() const = 0;
// Returns with changed=true if some values may have changed
virtual FillResult<Sci::Position> FillRange(Sci::Position position, int value, Sci::Position fillLength) = 0;
virtual void InsertSpace(Sci::Position position, Sci::Position insertLength) = 0;
virtual void DeleteRange(Sci::Position position, Sci::Position deleteLength) = 0;
virtual void DeleteLexerDecorations() = 0;
virtual int AllOnFor(Sci::Position position) const = 0;
virtual int ValueAt(int indicator, Sci::Position position) = 0;
virtual Sci::Position Start(int indicator, Sci::Position position) = 0;
virtual Sci::Position End(int indicator, Sci::Position position) = 0;
virtual bool ClickNotified() const = 0;
virtual void SetClickNotified(bool notified) = 0;
};
std::unique_ptr<IDecoration> DecorationCreate(bool largeDocument, int indicator);
std::unique_ptr<IDecorationList> DecorationListCreate(bool largeDocument);
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,584 @@
// Scintilla source code edit control
/** @file Document.h
** Text document that handles notifications, DBCS, styling, words and end of line.
**/
// Copyright 1998-2011 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
namespace Scintilla {
class DocWatcher;
class DocModification;
class Document;
class LineMarkers;
class LineLevels;
class LineState;
class LineAnnotation;
enum EncodingFamily { efEightBit, efUnicode, efDBCS };
/**
* 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:
Sci::Position start;
Sci::Position end;
explicit Range(Sci::Position pos=0) noexcept :
start(pos), end(pos) {
}
Range(Sci::Position start_, Sci::Position end_) noexcept :
start(start_), end(end_) {
}
bool operator==(const Range &other) const noexcept {
return (start == other.start) && (end == other.end);
}
bool Valid() const noexcept {
return (start != Sci::invalidPosition) && (end != Sci::invalidPosition);
}
Sci::Position First() const noexcept {
return (start <= end) ? start : end;
}
Sci::Position Last() const noexcept {
return (start > end) ? start : end;
}
// Is the position within the range?
bool Contains(Sci::Position pos) const noexcept {
if (start < end) {
return (pos >= start && pos <= end);
} else {
return (pos <= start && pos >= end);
}
}
// Is the character after pos within the range?
bool ContainsCharacter(Sci::Position pos) const noexcept {
if (start < end) {
return (pos >= start && pos < end);
} else {
return (pos < start && pos >= end);
}
}
bool Contains(Range other) const noexcept {
return Contains(other.start) && Contains(other.end);
}
bool Overlaps(Range other) const noexcept {
return
Contains(other.start) ||
Contains(other.end) ||
other.Contains(start) ||
other.Contains(end);
}
};
/**
* Interface class for regular expression searching
*/
class RegexSearchBase {
public:
virtual ~RegexSearchBase() {}
virtual Sci::Position FindText(Document *doc, Sci::Position minPos, Sci::Position maxPos, const char *s,
bool caseSensitive, bool word, bool wordStart, int flags, Sci::Position *length) = 0;
///@return String with the substitutions, must remain valid until the next call or destruction
virtual const char *SubstituteByPosition(Document *doc, const char *text, Sci::Position *length) = 0;
};
/// Factory function for RegexSearchBase
extern RegexSearchBase *CreateRegexSearch(CharClassify *charClassTable);
struct StyledText {
size_t length;
const char *text;
bool multipleStyles;
size_t style;
const unsigned char *styles;
StyledText(size_t length_, const char *text_, bool multipleStyles_, int style_, const unsigned char *styles_) noexcept :
length(length_), text(text_), multipleStyles(multipleStyles_), style(style_), styles(styles_) {
}
// Return number of bytes from start to before '\n' or end of text.
// Return 1 when start is outside text
size_t LineLength(size_t start) const noexcept {
size_t cur = start;
while ((cur < length) && (text[cur] != '\n'))
cur++;
return cur-start;
}
size_t StyleAt(size_t i) const noexcept {
return multipleStyles ? styles[i] : style;
}
};
class HighlightDelimiter {
public:
HighlightDelimiter() : isEnabled(false) {
Clear();
}
void Clear() {
beginFoldBlock = -1;
endFoldBlock = -1;
firstChangeableLineBefore = -1;
firstChangeableLineAfter = -1;
}
bool NeedsDrawing(Sci::Line line) const {
return isEnabled && (line <= firstChangeableLineBefore || line >= firstChangeableLineAfter);
}
bool IsFoldBlockHighlighted(Sci::Line line) const {
return isEnabled && beginFoldBlock != -1 && beginFoldBlock <= line && line <= endFoldBlock;
}
bool IsHeadOfFoldBlock(Sci::Line line) const {
return beginFoldBlock == line && line < endFoldBlock;
}
bool IsBodyOfFoldBlock(Sci::Line line) const {
return beginFoldBlock != -1 && beginFoldBlock < line && line < endFoldBlock;
}
bool IsTailOfFoldBlock(Sci::Line line) const {
return beginFoldBlock != -1 && beginFoldBlock < line && line == endFoldBlock;
}
Sci::Line beginFoldBlock; // Begin of current fold block
Sci::Line endFoldBlock; // End of current fold block
Sci::Line firstChangeableLineBefore; // First line that triggers repaint before starting line that determined current fold block
Sci::Line firstChangeableLineAfter; // First line that triggers repaint after starting line that determined current fold block
bool isEnabled;
};
inline int LevelNumber(int level) noexcept {
return level & SC_FOLDLEVELNUMBERMASK;
}
class LexInterface {
protected:
Document *pdoc;
ILexer *instance;
bool performingStyle; ///< Prevent reentrance
public:
explicit LexInterface(Document *pdoc_) : pdoc(pdoc_), instance(nullptr), performingStyle(false) {
}
virtual ~LexInterface() {
}
void Colourise(Sci::Position start, Sci::Position end);
virtual int LineEndTypesSupported();
bool UseContainerLexing() const {
return instance == nullptr;
}
};
struct RegexError : public std::runtime_error {
RegexError() : std::runtime_error("regex failure") {}
};
/**
* The ActionDuration class stores the average time taken for some action such as styling or
* wrapping a line. It is used to decide how many repetitions of that action can be performed
* on idle to maximize efficiency without affecting application responsiveness.
* The duration changes if the time for the action changes. For example, if a simple lexer is
* changed to a complex lexer. Changes are damped and clamped to avoid short periods of easy
* or difficult processing moving the value too far leading to inefficiency or poor user
* experience.
*/
class ActionDuration {
double duration;
const double minDuration;
const double maxDuration;
public:
ActionDuration(double duration_, double minDuration_, double maxDuration_) noexcept;
void AddSample(size_t numberActions, double durationOfActions) noexcept;
double Duration() const noexcept;
};
/**
*/
class Document : PerLine, public IDocumentWithLineEnd, public ILoader {
public:
/** Used to pair watcher pointer with user data. */
struct WatcherWithUserData {
DocWatcher *watcher;
void *userData;
WatcherWithUserData(DocWatcher *watcher_=nullptr, void *userData_=nullptr) noexcept :
watcher(watcher_), userData(userData_) {
}
bool operator==(const WatcherWithUserData &other) const noexcept {
return (watcher == other.watcher) && (userData == other.userData);
}
};
private:
int refCount;
CellBuffer cb;
CharClassify charClass;
std::unique_ptr<CaseFolder> pcf;
Sci::Position endStyled;
int styleClock;
int enteredModification;
int enteredStyling;
int enteredReadOnlyCount;
bool insertionSet;
std::string insertion;
std::vector<WatcherWithUserData> watchers;
// ldSize is not real data - it is for dimensions and loops
enum lineData { ldMarkers, ldLevels, ldState, ldMargin, ldAnnotation, ldSize };
std::unique_ptr<PerLine> perLineData[ldSize];
LineMarkers *Markers() const;
LineLevels *Levels() const;
LineState *States() const;
LineAnnotation *Margins() const;
LineAnnotation *Annotations() const;
bool matchesValid;
std::unique_ptr<RegexSearchBase> regex;
std::unique_ptr<LexInterface> pli;
public:
struct CharacterExtracted {
unsigned int character;
unsigned int widthBytes;
CharacterExtracted(unsigned int character_, unsigned int widthBytes_) noexcept :
character(character_), widthBytes(widthBytes_) {
}
// For DBCS characters turn 2 bytes into an int
static CharacterExtracted DBCS(unsigned char lead, unsigned char trail) noexcept {
return CharacterExtracted((lead << 8) | trail, 2);
}
};
int eolMode;
/// Can also be SC_CP_UTF8 to enable UTF-8 mode
int dbcsCodePage;
int lineEndBitSet;
int tabInChars;
int indentInChars;
int actualIndentInChars;
bool useTabs;
bool tabIndents;
bool backspaceUnindents;
ActionDuration durationStyleOneLine;
std::unique_ptr<IDecorationList> decorations;
Document(int options);
// Deleted so Document objects can not be copied.
Document(const Document &) = delete;
Document(Document &&) = delete;
void operator=(const Document &) = delete;
Document &operator=(Document &&) = delete;
~Document() override;
int AddRef();
int SCI_METHOD Release() override;
// From PerLine
void Init() override;
void InsertLine(Sci::Line line) override;
void RemoveLine(Sci::Line line) override;
int LineEndTypesSupported() const;
bool SetDBCSCodePage(int dbcsCodePage_);
int GetLineEndTypesAllowed() const { return cb.GetLineEndTypes(); }
bool SetLineEndTypesAllowed(int lineEndBitSet_);
int GetLineEndTypesActive() const { return cb.GetLineEndTypes(); }
int SCI_METHOD Version() const override {
return dvLineEnd;
}
void SCI_METHOD SetErrorStatus(int status) override;
Sci_Position SCI_METHOD LineFromPosition(Sci_Position pos) const override;
Sci::Line SciLineFromPosition(Sci::Position pos) const noexcept; // Avoids casting LineFromPosition
Sci::Position ClampPositionIntoDocument(Sci::Position pos) const;
bool ContainsLineEnd(const char *s, Sci::Position length) const { return cb.ContainsLineEnd(s, length); }
bool IsCrLf(Sci::Position pos) const;
int LenChar(Sci::Position pos);
bool InGoodUTF8(Sci::Position pos, Sci::Position &start, Sci::Position &end) const noexcept;
Sci::Position MovePositionOutsideChar(Sci::Position pos, Sci::Position moveDir, bool checkLineEnd=true) const;
Sci::Position NextPosition(Sci::Position pos, int moveDir) const noexcept;
bool NextCharacter(Sci::Position &pos, int moveDir) const noexcept; // Returns true if pos changed
Document::CharacterExtracted CharacterAfter(Sci::Position position) const;
Document::CharacterExtracted CharacterBefore(Sci::Position position) const;
Sci_Position SCI_METHOD GetRelativePosition(Sci_Position positionStart, Sci_Position characterOffset) const override;
Sci::Position GetRelativePositionUTF16(Sci::Position positionStart, Sci::Position characterOffset) const;
int SCI_METHOD GetCharacterAndWidth(Sci_Position position, Sci_Position *pWidth) const override;
int SCI_METHOD CodePage() const override;
bool SCI_METHOD IsDBCSLeadByte(char ch) const override;
bool IsDBCSLeadByteNoExcept(char ch) const noexcept;
bool IsDBCSLeadByteInvalid(char ch) const noexcept;
bool IsDBCSTrailByteInvalid(char ch) const noexcept;
int DBCSDrawBytes(const char *text, int len) const noexcept;
int SafeSegment(const char *text, int length, int lengthSegment) const noexcept;
EncodingFamily CodePageFamily() const noexcept;
// Gateways to modifying document
void ModifiedAt(Sci::Position pos) noexcept;
void CheckReadOnly();
bool DeleteChars(Sci::Position pos, Sci::Position len);
Sci::Position InsertString(Sci::Position position, const char *s, Sci::Position insertLength);
void ChangeInsertion(const char *s, Sci::Position length);
int SCI_METHOD AddData(const char *data, Sci_Position length) override;
void * SCI_METHOD ConvertToDocument() override;
Sci::Position Undo();
Sci::Position Redo();
bool CanUndo() const { return cb.CanUndo(); }
bool CanRedo() const { return cb.CanRedo(); }
void DeleteUndoHistory() { cb.DeleteUndoHistory(); }
bool SetUndoCollection(bool collectUndo) {
return cb.SetUndoCollection(collectUndo);
}
bool IsCollectingUndo() const { return cb.IsCollectingUndo(); }
void BeginUndoAction() { cb.BeginUndoAction(); }
void EndUndoAction() { cb.EndUndoAction(); }
void AddUndoAction(Sci::Position token, bool mayCoalesce) { cb.AddUndoAction(token, mayCoalesce); }
void SetSavePoint();
bool IsSavePoint() const { return cb.IsSavePoint(); }
void TentativeStart() { cb.TentativeStart(); }
void TentativeCommit() { cb.TentativeCommit(); }
void TentativeUndo();
bool TentativeActive() const { return cb.TentativeActive(); }
const char * SCI_METHOD BufferPointer() override { return cb.BufferPointer(); }
const char *RangePointer(Sci::Position position, Sci::Position rangeLength) { return cb.RangePointer(position, rangeLength); }
Sci::Position GapPosition() const { return cb.GapPosition(); }
int SCI_METHOD GetLineIndentation(Sci_Position line) override;
Sci::Position SetLineIndentation(Sci::Line line, Sci::Position indent);
Sci::Position GetLineIndentPosition(Sci::Line line) const;
Sci::Position GetColumn(Sci::Position pos);
Sci::Position CountCharacters(Sci::Position startPos, Sci::Position endPos) const;
Sci::Position CountUTF16(Sci::Position startPos, Sci::Position endPos) const;
Sci::Position FindColumn(Sci::Line line, Sci::Position column);
void Indent(bool forwards, Sci::Line lineBottom, Sci::Line lineTop);
static std::string TransformLineEnds(const char *s, size_t len, int eolModeWanted);
void ConvertLineEnds(int eolModeSet);
void SetReadOnly(bool set) { cb.SetReadOnly(set); }
bool IsReadOnly() const { return cb.IsReadOnly(); }
bool IsLarge() const { return cb.IsLarge(); }
int Options() const;
void DelChar(Sci::Position pos);
void DelCharBack(Sci::Position pos);
char CharAt(Sci::Position position) const noexcept { return cb.CharAt(position); }
void SCI_METHOD GetCharRange(char *buffer, Sci_Position position, Sci_Position lengthRetrieve) const override {
cb.GetCharRange(buffer, position, lengthRetrieve);
}
char SCI_METHOD StyleAt(Sci_Position position) const override { return cb.StyleAt(position); }
int StyleIndexAt(Sci_Position position) const noexcept { return static_cast<unsigned char>(cb.StyleAt(position)); }
void GetStyleRange(unsigned char *buffer, Sci::Position position, Sci::Position lengthRetrieve) const {
cb.GetStyleRange(buffer, position, lengthRetrieve);
}
int GetMark(Sci::Line line) const;
Sci::Line MarkerNext(Sci::Line lineStart, int mask) const;
int AddMark(Sci::Line line, int markerNum);
void AddMarkSet(Sci::Line line, int valueSet);
void DeleteMark(Sci::Line line, int markerNum);
void DeleteMarkFromHandle(int markerHandle);
void DeleteAllMarks(int markerNum);
Sci::Line LineFromHandle(int markerHandle) const;
Sci_Position SCI_METHOD LineStart(Sci_Position line) const override;
bool IsLineStartPosition(Sci::Position position) const;
Sci_Position SCI_METHOD LineEnd(Sci_Position line) const override;
Sci::Position LineEndPosition(Sci::Position position) const;
bool IsLineEndPosition(Sci::Position position) const;
bool IsPositionInLineEnd(Sci::Position position) const;
Sci::Position VCHomePosition(Sci::Position position) const;
Sci::Position IndexLineStart(Sci::Line line, int lineCharacterIndex) const;
Sci::Line LineFromPositionIndex(Sci::Position pos, int lineCharacterIndex) const;
int SCI_METHOD SetLevel(Sci_Position line, int level) override;
int SCI_METHOD GetLevel(Sci_Position line) const override;
void ClearLevels();
Sci::Line GetLastChild(Sci::Line lineParent, int level=-1, Sci::Line lastLine=-1);
Sci::Line GetFoldParent(Sci::Line line) const;
void GetHighlightDelimiters(HighlightDelimiter &highlightDelimiter, Sci::Line line, Sci::Line lastLine);
Sci::Position ExtendWordSelect(Sci::Position pos, int delta, bool onlyWordCharacters=false) const;
Sci::Position NextWordStart(Sci::Position pos, int delta) const;
Sci::Position NextWordEnd(Sci::Position pos, int delta) const;
Sci_Position SCI_METHOD Length() const override { return cb.Length(); }
void Allocate(Sci::Position newSize) { cb.Allocate(newSize); }
CharacterExtracted ExtractCharacter(Sci::Position position) const noexcept;
bool IsWordStartAt(Sci::Position pos) const;
bool IsWordEndAt(Sci::Position pos) const;
bool IsWordAt(Sci::Position start, Sci::Position end) const;
bool MatchesWordOptions(bool word, bool wordStart, Sci::Position pos, Sci::Position length) const;
bool HasCaseFolder() const noexcept;
void SetCaseFolder(CaseFolder *pcf_);
Sci::Position FindText(Sci::Position minPos, Sci::Position maxPos, const char *search, int flags, Sci::Position *length);
const char *SubstituteByPosition(const char *text, Sci::Position *length);
int LineCharacterIndex() const;
void AllocateLineCharacterIndex(int lineCharacterIndex);
void ReleaseLineCharacterIndex(int lineCharacterIndex);
Sci::Line LinesTotal() const noexcept;
void SetDefaultCharClasses(bool includeWordClass);
void SetCharClasses(const unsigned char *chars, CharClassify::cc newCharClass);
int GetCharsOfClass(CharClassify::cc characterClass, unsigned char *buffer) const;
void SCI_METHOD StartStyling(Sci_Position position, char mask) override;
bool SCI_METHOD SetStyleFor(Sci_Position length, char style) override;
bool SCI_METHOD SetStyles(Sci_Position length, const char *styles) override;
Sci::Position GetEndStyled() const noexcept { return endStyled; }
void EnsureStyledTo(Sci::Position pos);
void StyleToAdjustingLineDuration(Sci::Position pos);
void LexerChanged();
int GetStyleClock() const noexcept { return styleClock; }
void IncrementStyleClock() noexcept;
void SCI_METHOD DecorationSetCurrentIndicator(int indicator) override;
void SCI_METHOD DecorationFillRange(Sci_Position position, int value, Sci_Position fillLength) override;
LexInterface *GetLexInterface() const;
void SetLexInterface(LexInterface *pLexInterface);
int SCI_METHOD SetLineState(Sci_Position line, int state) override;
int SCI_METHOD GetLineState(Sci_Position line) const override;
Sci::Line GetMaxLineState() const;
void SCI_METHOD ChangeLexerState(Sci_Position start, Sci_Position end) override;
StyledText MarginStyledText(Sci::Line line) const;
void MarginSetStyle(Sci::Line line, int style);
void MarginSetStyles(Sci::Line line, const unsigned char *styles);
void MarginSetText(Sci::Line line, const char *text);
void MarginClearAll();
StyledText AnnotationStyledText(Sci::Line line) const;
void AnnotationSetText(Sci::Line line, const char *text);
void AnnotationSetStyle(Sci::Line line, int style);
void AnnotationSetStyles(Sci::Line line, const unsigned char *styles);
int AnnotationLines(Sci::Line line) const;
void AnnotationClearAll();
bool AddWatcher(DocWatcher *watcher, void *userData);
bool RemoveWatcher(DocWatcher *watcher, void *userData);
bool IsASCIIWordByte(unsigned char ch) const;
CharClassify::cc WordCharacterClass(unsigned int ch) const;
bool IsWordPartSeparator(unsigned int ch) const;
Sci::Position WordPartLeft(Sci::Position pos) const;
Sci::Position WordPartRight(Sci::Position pos) const;
Sci::Position ExtendStyleRange(Sci::Position pos, int delta, bool singleLine = false);
bool IsWhiteLine(Sci::Line line) const;
Sci::Position ParaUp(Sci::Position pos) const;
Sci::Position ParaDown(Sci::Position pos) const;
int IndentSize() const noexcept { return actualIndentInChars; }
Sci::Position BraceMatch(Sci::Position position, Sci::Position maxReStyle);
private:
void NotifyModifyAttempt();
void NotifySavePoint(bool atSavePoint);
void NotifyModified(DocModification mh);
};
class UndoGroup {
Document *pdoc;
bool groupNeeded;
public:
UndoGroup(Document *pdoc_, bool groupNeeded_=true) :
pdoc(pdoc_), groupNeeded(groupNeeded_) {
if (groupNeeded) {
pdoc->BeginUndoAction();
}
}
~UndoGroup() {
if (groupNeeded) {
pdoc->EndUndoAction();
}
}
bool Needed() const noexcept {
return groupNeeded;
}
};
/**
* 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;
Sci::Position position;
Sci::Position length;
Sci::Line linesAdded; /**< Negative if lines deleted. */
const char *text; /**< Only valid for changes to text, not for changes to style. */
Sci::Line line;
int foldLevelNow;
int foldLevelPrev;
Sci::Line annotationLinesAdded;
Sci::Position token;
DocModification(int modificationType_, Sci::Position position_=0, Sci::Position length_=0,
Sci::Line linesAdded_=0, const char *text_=nullptr, Sci::Line line_=0) noexcept :
modificationType(modificationType_),
position(position_),
length(length_),
linesAdded(linesAdded_),
text(text_),
line(line_),
foldLevelNow(0),
foldLevelPrev(0),
annotationLinesAdded(0),
token(0) {}
DocModification(int modificationType_, const Action &act, Sci::Line linesAdded_=0) noexcept :
modificationType(modificationType_),
position(act.position),
length(act.lenData),
linesAdded(linesAdded_),
text(act.data.get()),
line(0),
foldLevelNow(0),
foldLevelPrev(0),
annotationLinesAdded(0),
token(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, Sci::Position endPos) = 0;
virtual void NotifyLexerChanged(Document *doc, void *userData) = 0;
virtual void NotifyErrorOccurred(Document *doc, void *userData, int status) = 0;
};
}
#endif

View File

@@ -0,0 +1,77 @@
// Scintilla source code edit control
/** @file EditModel.cxx
** Defines the editor state that must be visible to EditorView.
**/
// Copyright 1998-2014 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstddef>
#include <cstdlib>
#include <cassert>
#include <cstring>
#include <cmath>
#include <stdexcept>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <memory>
#include "Platform.h"
#include "ILoader.h"
#include "ILexer.h"
#include "Scintilla.h"
#include "Position.h"
#include "UniqueString.h"
#include "SplitVector.h"
#include "Partitioning.h"
#include "RunStyles.h"
#include "ContractionState.h"
#include "CellBuffer.h"
#include "KeyMap.h"
#include "Indicator.h"
#include "LineMarker.h"
#include "Style.h"
#include "ViewStyle.h"
#include "CharClassify.h"
#include "Decoration.h"
#include "CaseFolder.h"
#include "Document.h"
#include "UniConversion.h"
#include "Selection.h"
#include "PositionCache.h"
#include "EditModel.h"
using namespace Scintilla;
Caret::Caret() :
active(false), on(false), period(500) {}
EditModel::EditModel() : braces{} {
inOverstrike = false;
xOffset = 0;
trackLineWidth = false;
posDrag = SelectionPosition(Sci::invalidPosition);
braces[0] = Sci::invalidPosition;
braces[1] = Sci::invalidPosition;
bracesMatchStyle = STYLE_BRACEBAD;
highlightGuideColumn = 0;
primarySelection = true;
imeInteraction = imeWindowed;
foldFlags = 0;
foldDisplayTextStyle = SC_FOLDDISPLAYTEXT_HIDDEN;
hotspot = Range(Sci::invalidPosition);
hoverIndicatorPos = Sci::invalidPosition;
wrapWidth = LineLayout::wrapWidthInfinite;
pdoc = new Document(SC_DOCUMENTOPTION_DEFAULT);
pdoc->AddRef();
pcs = ContractionStateCreate(pdoc->IsLarge());
}
EditModel::~EditModel() {
pdoc->Release();
pdoc = nullptr;
}

View File

@@ -0,0 +1,68 @@
// Scintilla source code edit control
/** @file EditModel.h
** Defines the editor state that must be visible to EditorView.
**/
// Copyright 1998-2014 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef EDITMODEL_H
#define EDITMODEL_H
namespace Scintilla {
/**
*/
class Caret {
public:
bool active;
bool on;
int period;
Caret();
};
class EditModel {
public:
bool inOverstrike;
int xOffset; ///< Horizontal scrolled amount in pixels
bool trackLineWidth;
SpecialRepresentations reprs;
Caret caret;
SelectionPosition posDrag;
Sci::Position braces[2];
int bracesMatchStyle;
int highlightGuideColumn;
Selection sel;
bool primarySelection;
enum IMEInteraction { imeWindowed, imeInline } imeInteraction;
int foldFlags;
int foldDisplayTextStyle;
std::unique_ptr<IContractionState> pcs;
// Hotspot support
Range hotspot;
Sci::Position hoverIndicatorPos;
// Wrapping support
int wrapWidth;
Document *pdoc;
EditModel();
// Deleted so EditModel objects can not be copied.
EditModel(const EditModel &) = delete;
EditModel(EditModel &&) = delete;
EditModel &operator=(const EditModel &) = delete;
EditModel &operator=(EditModel &&) = delete;
virtual ~EditModel();
virtual Sci::Line TopLineOfMain() const = 0;
virtual Point GetVisibleOriginInMain() const = 0;
virtual Sci::Line LinesOnScreen() const = 0;
virtual Range GetHotSpotRange() const = 0;
};
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,186 @@
// Scintilla source code edit control
/** @file EditView.h
** Defines the appearance of the main text area of the editor window.
**/
// Copyright 1998-2014 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef EDITVIEW_H
#define EDITVIEW_H
namespace Scintilla {
struct PrintParameters {
int magnification;
int colourMode;
WrapMode wrapState;
PrintParameters();
};
/**
* The view may be drawn in separate phases.
*/
enum DrawPhase {
drawBack = 0x1,
drawIndicatorsBack = 0x2,
drawText = 0x4,
drawIndentationGuides = 0x8,
drawIndicatorsFore = 0x10,
drawSelectionTranslucent = 0x20,
drawLineTranslucent = 0x40,
drawFoldLines = 0x80,
drawCarets = 0x100,
drawAll = 0x1FF
};
bool ValidStyledText(const ViewStyle &vs, size_t styleOffset, const StyledText &st);
int WidestLineWidth(Surface *surface, const ViewStyle &vs, int styleOffset, const StyledText &st);
void DrawTextNoClipPhase(Surface *surface, PRectangle rc, const Style &style, XYPOSITION ybase,
const char *s, int len, DrawPhase phase);
void DrawStyledText(Surface *surface, const ViewStyle &vs, int styleOffset, PRectangle rcText,
const StyledText &st, size_t start, size_t length, DrawPhase phase);
typedef void (*DrawTabArrowFn)(Surface *surface, PRectangle rcTab, int ymid);
class LineTabstops;
/**
* EditView draws the main text area.
*/
class EditView {
public:
PrintParameters printParameters;
std::unique_ptr<LineTabstops> ldTabstops;
int tabWidthMinimumPixels;
bool hideSelection;
bool drawOverstrikeCaret;
/** 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 phasesTwo mode, drawing is performed in two phases, first the background
* and then the foreground. This avoids chopping off characters that overlap the next run.
* In multiPhaseDraw mode, drawing is performed in multiple phases with each phase drawing
* one feature over the whole drawing area, instead of within one line. This allows text to
* overlap from one line to the next. */
enum PhasesDraw { phasesOne, phasesTwo, phasesMultiple };
PhasesDraw phasesDraw;
int lineWidthMaxSeen;
bool additionalCaretsBlink;
bool additionalCaretsVisible;
bool imeCaretBlockOverride;
std::unique_ptr<Surface> pixmapLine;
std::unique_ptr<Surface> pixmapIndentGuide;
std::unique_ptr<Surface> pixmapIndentGuideHighlight;
LineLayoutCache llc;
PositionCache posCache;
int tabArrowHeight; // draw arrow heads this many pixels above/below line midpoint
/** Some platforms, notably PLAT_CURSES, do not support Scintilla's native
* DrawTabArrow function for drawing tab characters. Allow those platforms to
* override it instead of creating a new method in the Surface class that
* existing platforms must implement as empty. */
DrawTabArrowFn customDrawTabArrow;
DrawWrapMarkerFn customDrawWrapMarker;
EditView();
// Deleted so EditView objects can not be copied.
EditView(const EditView &) = delete;
EditView(EditView &&) = delete;
void operator=(const EditView &) = delete;
void operator=(EditView &&) = delete;
virtual ~EditView();
bool SetTwoPhaseDraw(bool twoPhaseDraw);
bool SetPhasesDraw(int phases);
bool LinesOverlap() const;
void ClearAllTabstops();
XYPOSITION NextTabstopPos(Sci::Line line, XYPOSITION x, XYPOSITION tabWidth) const;
bool ClearTabstops(Sci::Line line);
bool AddTabstop(Sci::Line line, int x);
int GetNextTabstop(Sci::Line line, int x) const;
void LinesAddedOrRemoved(Sci::Line lineOfPos, Sci::Line linesAdded);
void DropGraphics(bool freeObjects);
void AllocateGraphics(const ViewStyle &vsDraw);
void RefreshPixMaps(Surface *surfaceWindow, WindowID wid, const ViewStyle &vsDraw);
LineLayout *RetrieveLineLayout(Sci::Line lineNumber, const EditModel &model);
void LayoutLine(const EditModel &model, Sci::Line line, Surface *surface, const ViewStyle &vstyle,
LineLayout *ll, int width = LineLayout::wrapWidthInfinite);
Point LocationFromPosition(Surface *surface, const EditModel &model, SelectionPosition pos, Sci::Line topLine,
const ViewStyle &vs, PointEnd pe);
Range RangeDisplayLine(Surface *surface, const EditModel &model, Sci::Line lineVisible, const ViewStyle &vs);
SelectionPosition SPositionFromLocation(Surface *surface, const EditModel &model, PointDocument pt, bool canReturnInvalid,
bool charPosition, bool virtualSpace, const ViewStyle &vs);
SelectionPosition SPositionFromLineX(Surface *surface, const EditModel &model, Sci::Line lineDoc, int x, const ViewStyle &vs);
Sci::Line DisplayFromPosition(Surface *surface, const EditModel &model, Sci::Position pos, const ViewStyle &vs);
Sci::Position StartEndDisplayLine(Surface *surface, const EditModel &model, Sci::Position pos, bool start, const ViewStyle &vs);
void DrawIndentGuide(Surface *surface, Sci::Line lineVisible, int lineHeight, XYPOSITION start, PRectangle rcSegment, bool highlight);
void DrawEOL(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, PRectangle rcLine,
Sci::Line line, Sci::Position lineEnd, int xStart, int subLine, XYACCUMULATOR subLineStart,
ColourOptional background);
void DrawFoldDisplayText(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
Sci::Line line, int xStart, PRectangle rcLine, int subLine, XYACCUMULATOR subLineStart, DrawPhase phase);
void DrawAnnotation(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
Sci::Line line, int xStart, PRectangle rcLine, int subLine, DrawPhase phase);
void DrawCarets(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, Sci::Line lineDoc,
int xStart, PRectangle rcLine, int subLine) const;
void DrawBackground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, PRectangle rcLine,
Range lineRange, Sci::Position posLineStart, int xStart,
int subLine, ColourOptional background) const;
void DrawForeground(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, Sci::Line lineVisible,
PRectangle rcLine, Range lineRange, Sci::Position posLineStart, int xStart,
int subLine, ColourOptional background);
void DrawIndentGuidesOverEmpty(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
Sci::Line line, Sci::Line lineVisible, PRectangle rcLine, int xStart, int subLine);
void DrawLine(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll, Sci::Line line,
Sci::Line lineVisible, int xStart, PRectangle rcLine, int subLine, DrawPhase phase);
void PaintText(Surface *surfaceWindow, const EditModel &model, PRectangle rcArea, PRectangle rcClient,
const ViewStyle &vsDraw);
void FillLineRemainder(Surface *surface, const EditModel &model, const ViewStyle &vsDraw, const LineLayout *ll,
Sci::Line line, PRectangle rcArea, int subLine) const;
Sci::Position FormatRange(bool draw, const Sci_RangeToFormat *pfr, Surface *surface, Surface *surfaceMeasure,
const EditModel &model, const ViewStyle &vs);
};
/**
* Convenience class to ensure LineLayout objects are always disposed.
*/
class AutoLineLayout {
LineLayoutCache &llc;
LineLayout *ll;
public:
AutoLineLayout(LineLayoutCache &llc_, LineLayout *ll_) noexcept : llc(llc_), ll(ll_) {}
AutoLineLayout(const AutoLineLayout &) = delete;
AutoLineLayout(AutoLineLayout &&) = delete;
AutoLineLayout &operator=(const AutoLineLayout &) = delete;
AutoLineLayout &operator=(AutoLineLayout &&) = delete;
~AutoLineLayout() {
llc.Dispose(ll);
ll = nullptr;
}
LineLayout *operator->() const noexcept {
return ll;
}
operator LineLayout *() const noexcept {
return ll;
}
void Set(LineLayout *ll_) {
llc.Dispose(ll);
ll = ll_;
}
};
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,650 @@
// Scintilla source code edit control
/** @file Editor.h
** Defines the main editor class.
**/
// Copyright 1998-2011 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
namespace Scintilla {
/**
*/
class Timer {
public:
bool ticking;
int ticksToWait;
enum {tickSize = 100};
TickerID tickerID;
Timer();
};
/**
*/
class Idler {
public:
bool state;
IdlerID idlerID;
Idler();
};
/**
* When platform has a way to generate an event before painting,
* accumulate needed styling range and other work items in
* WorkNeeded to avoid unnecessary work inside paint handler
*/
class WorkNeeded {
public:
enum workItems {
workNone=0,
workStyle=1,
workUpdateUI=2
};
enum workItems items;
Sci::Position upTo;
WorkNeeded() : items(workNone), upTo(0) {}
void Reset() {
items = workNone;
upTo = 0;
}
void Need(workItems items_, Sci::Position pos) {
if ((items_ & workStyle) && (upTo < pos))
upTo = pos;
items = static_cast<workItems>(items | items_);
}
};
/**
* Hold a piece of text selected for copying or dragging, along with encoding and selection format information.
*/
class SelectionText {
std::string s;
public:
bool rectangular;
bool lineCopy;
int codePage;
int characterSet;
SelectionText() : rectangular(false), lineCopy(false), codePage(0), characterSet(0) {}
void Clear() {
s.clear();
rectangular = false;
lineCopy = false;
codePage = 0;
characterSet = 0;
}
void Copy(const std::string &s_, int codePage_, int characterSet_, bool rectangular_, bool lineCopy_) {
s = s_;
codePage = codePage_;
characterSet = characterSet_;
rectangular = rectangular_;
lineCopy = lineCopy_;
FixSelectionForClipboard();
}
void Copy(const SelectionText &other) {
Copy(other.s, other.codePage, other.characterSet, other.rectangular, other.lineCopy);
}
const char *Data() const {
return s.c_str();
}
size_t Length() const {
return s.length();
}
size_t LengthWithTerminator() const {
return s.length() + 1;
}
bool Empty() const {
return s.empty();
}
private:
void FixSelectionForClipboard() {
// To avoid truncating the contents of the clipboard when pasted where the
// clipboard contains NUL characters, replace NUL characters by spaces.
std::replace(s.begin(), s.end(), '\0', ' ');
}
};
struct WrapPending {
// The range of lines that need to be wrapped
enum { lineLarge = 0x7ffffff };
Sci::Line start; // When there are wraps pending, will be in document range
Sci::Line end; // May be lineLarge to indicate all of document after start
WrapPending() {
start = lineLarge;
end = lineLarge;
}
void Reset() {
start = lineLarge;
end = lineLarge;
}
void Wrapped(Sci::Line line) {
if (start == line)
start++;
}
bool NeedsWrap() const {
return start < end;
}
bool AddRange(Sci::Line lineStart, Sci::Line lineEnd) {
const bool neededWrap = NeedsWrap();
bool changed = false;
if (start > lineStart) {
start = lineStart;
changed = true;
}
if ((end < lineEnd) || !neededWrap) {
end = lineEnd;
changed = true;
}
return changed;
}
};
/**
*/
class Editor : public EditModel, public DocWatcher {
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
Window wMargin; ///< May be separate when using a scroll view for wMain
/** 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;
int technology;
Point sizeRGBAImage;
float scaleRGBAImage;
MarginView marginView;
EditView view;
int cursorMode;
bool hasFocus;
bool mouseDownCaptures;
bool mouseWheelCaptures;
int xCaretMargin; ///< Ensure this many pixels visible on both sides of caret
bool horizontalScrollBarVisible;
int scrollWidth;
bool verticalScrollBarVisible;
bool endAtLastLine;
int caretSticky;
int marginOptions;
bool mouseSelectionRectangularSwitch;
bool multipleSelection;
bool additionalSelectionTyping;
int multiPasteMode;
int virtualSpaceOptions;
KeyMap kmap;
Timer timer;
Timer autoScrollTimer;
enum { autoScrollDelay = 200 };
Idler idler;
Point lastClick;
unsigned int lastClickTime;
Point doubleClickCloseThreshold;
int dwellDelay;
int ticksToDwell;
bool dwelling;
enum { selChar, selWord, selSubLine, selWholeLine } selectionType;
Point ptMouseLast;
enum { ddNone, ddInitial, ddDragging } inDragDrop;
bool dropWentOutside;
SelectionPosition posDrop;
Sci::Position hotSpotClickPos;
int lastXChosen;
Sci::Position lineAnchorPos;
Sci::Position originalAnchorPos;
Sci::Position wordSelectAnchorStartPos;
Sci::Position wordSelectAnchorEndPos;
Sci::Position wordSelectInitialCaretPos;
Sci::Position targetStart;
Sci::Position targetEnd;
int searchFlags;
Sci::Line topLine;
Sci::Position posTopLine;
Sci::Position lengthForEncode;
int needUpdateUI;
enum { notPainting, painting, paintAbandoned } paintState;
bool paintAbandonedByStyling;
PRectangle rcPaint;
bool paintingAllText;
bool willRedrawAll;
WorkNeeded workNeeded;
int idleStyling;
bool needIdleStyling;
int modEventMask;
bool commandEvents;
SelectionText drag;
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;
Sci::Position searchAnchor;
bool recordingMacro;
int foldAutomatic;
// Wrapping support
WrapPending wrapPending;
ActionDuration durationWrapOneLine;
bool convertPastes;
Editor();
// Deleted so Editor objects can not be copied.
Editor(const Editor &) = delete;
Editor(Editor &&) = delete;
Editor &operator=(const Editor &) = delete;
Editor &operator=(Editor &&) = delete;
~Editor() override;
virtual void Initialise() = 0;
virtual void Finalise();
void InvalidateStyleData();
void InvalidateStyleRedraw();
void RefreshStyleData();
void SetRepresentations();
void DropGraphics(bool freeObjects);
void AllocateGraphics();
// The top left visible point in main window coordinates. Will be 0,0 except for
// scroll views where it will be equivalent to the current scroll position.
Point GetVisibleOriginInMain() const override;
PointDocument DocumentPointFromView(Point ptView) const; // Convert a point from view space to document
Sci::Line TopLineOfMain() const override; // Return the line at Main's y coordinate 0
virtual PRectangle GetClientRectangle() const;
virtual PRectangle GetClientDrawingRectangle();
PRectangle GetTextRectangle() const;
Sci::Line LinesOnScreen() const override;
Sci::Line LinesToScroll() const;
Sci::Line MaxScrollPos() const;
SelectionPosition ClampPositionIntoDocument(SelectionPosition sp) const;
Point LocationFromPosition(SelectionPosition pos, PointEnd pe=peDefault);
Point LocationFromPosition(Sci::Position pos, PointEnd pe=peDefault);
int XFromPosition(SelectionPosition sp);
SelectionPosition SPositionFromLocation(Point pt, bool canReturnInvalid=false, bool charPosition=false, bool virtualSpace=true);
Sci::Position PositionFromLocation(Point pt, bool canReturnInvalid = false, bool charPosition = false);
SelectionPosition SPositionFromLineX(Sci::Line lineDoc, int x);
Sci::Position PositionFromLineX(Sci::Line lineDoc, int x);
Sci::Line LineFromLocation(Point pt) const;
void SetTopLine(Sci::Line topLineNew);
virtual bool AbandonPaint();
virtual void RedrawRect(PRectangle rc);
virtual void DiscardOverdraw();
virtual void Redraw();
void RedrawSelMargin(Sci::Line line=-1, bool allAfter=false);
PRectangle RectangleFromRange(Range r, int overlap);
void InvalidateRange(Sci::Position start, Sci::Position end);
bool UserVirtualSpace() const {
return ((virtualSpaceOptions & SCVS_USERACCESSIBLE) != 0);
}
Sci::Position CurrentPosition() const;
bool SelectionEmpty() const;
SelectionPosition SelectionStart();
SelectionPosition SelectionEnd();
void SetRectangularRange();
void ThinRectangularRange();
void InvalidateSelection(SelectionRange newMain, bool invalidateWholeSelection=false);
void InvalidateWholeSelection();
SelectionRange LineSelectionRange(SelectionPosition currentPos_, SelectionPosition anchor_) const;
void SetSelection(SelectionPosition currentPos_, SelectionPosition anchor_);
void SetSelection(Sci::Position currentPos_, Sci::Position anchor_);
void SetSelection(SelectionPosition currentPos_);
void SetSelection(int currentPos_);
void SetEmptySelection(SelectionPosition currentPos_);
void SetEmptySelection(Sci::Position currentPos_);
enum AddNumber { addOne, addEach };
void MultipleSelectAdd(AddNumber addNumber);
bool RangeContainsProtected(Sci::Position start, Sci::Position end) const;
bool SelectionContainsProtected();
Sci::Position MovePositionOutsideChar(Sci::Position pos, Sci::Position moveDir, bool checkLineEnd=true) const;
SelectionPosition MovePositionOutsideChar(SelectionPosition pos, Sci::Position moveDir, bool checkLineEnd=true) const;
void MovedCaret(SelectionPosition newPos, SelectionPosition previousPos, bool ensureVisible);
void MovePositionTo(SelectionPosition newPos, Selection::selTypes selt=Selection::noSel, bool ensureVisible=true);
void MovePositionTo(Sci::Position newPos, Selection::selTypes selt=Selection::noSel, bool ensureVisible=true);
SelectionPosition MovePositionSoVisible(SelectionPosition pos, int moveDir);
SelectionPosition MovePositionSoVisible(Sci::Position pos, int moveDir);
Point PointMainCaret();
void SetLastXChosen();
void ScrollTo(Sci::Line line, bool moveThumb=true);
virtual void ScrollText(Sci::Line linesToMove);
void HorizontalScrollTo(int xPos);
void VerticalCentreCaret();
void MoveSelectedLines(int lineDelta);
void MoveSelectedLinesUp();
void MoveSelectedLinesDown();
void MoveCaretInsideView(bool ensureVisible=true);
Sci::Line DisplayFromPosition(Sci::Position pos);
struct XYScrollPosition {
int xOffset;
Sci::Line topLine;
XYScrollPosition(int xOffset_, Sci::Line topLine_) : xOffset(xOffset_), topLine(topLine_) {}
bool operator==(const XYScrollPosition &other) const {
return (xOffset == other.xOffset) && (topLine == other.topLine);
}
};
enum XYScrollOptions {
xysUseMargin=0x1,
xysVertical=0x2,
xysHorizontal=0x4,
xysDefault=xysUseMargin|xysVertical|xysHorizontal};
XYScrollPosition XYScrollToMakeVisible(const SelectionRange &range, const XYScrollOptions options);
void SetXYScroll(XYScrollPosition newXY);
void EnsureCaretVisible(bool useMargin=true, bool vert=true, bool horiz=true);
void ScrollRange(SelectionRange range);
void ShowCaretAtCurrentPosition();
void DropCaret();
void CaretSetPeriod(int period);
void InvalidateCaret();
virtual void NotifyCaretMove();
virtual void UpdateSystemCaret();
bool Wrapping() const;
void NeedWrapping(Sci::Line docLineStart=0, Sci::Line docLineEnd=WrapPending::lineLarge);
bool WrapOneLine(Surface *surface, Sci::Line lineToWrap);
enum class WrapScope {wsAll, wsVisible, wsIdle};
bool WrapLines(WrapScope ws);
void LinesJoin();
void LinesSplit(int pixelWidth);
void PaintSelMargin(Surface *surfaceWindow, const PRectangle &rc);
void RefreshPixMaps(Surface *surfaceWindow);
void Paint(Surface *surfaceWindow, PRectangle rcArea);
Sci::Position FormatRange(bool draw, const Sci_RangeToFormat *pfr);
int TextWidth(int style, const char *text);
virtual void SetVerticalScrollPos() = 0;
virtual void SetHorizontalScrollPos() = 0;
virtual bool ModifyScrollBars(Sci::Line nMax, Sci::Line nPage) = 0;
virtual void ReconfigureScrollBars();
void SetScrollBars();
void ChangeSize();
void FilterSelections();
Sci::Position RealizeVirtualSpace(Sci::Position position, Sci::Position virtualSpace);
SelectionPosition RealizeVirtualSpace(const SelectionPosition &position);
void AddChar(char ch);
virtual void AddCharUTF(const char *s, unsigned int len, bool treatAsDBCS=false);
void ClearBeforeTentativeStart();
void InsertPaste(const char *text, Sci::Position len);
enum PasteShape { pasteStream=0, pasteRectangular = 1, pasteLine = 2 };
void InsertPasteShape(const char *text, Sci::Position len, PasteShape shape);
void ClearSelection(bool retainMultipleSelections = false);
void ClearAll();
void ClearDocumentStyle();
virtual void Cut();
void PasteRectangular(SelectionPosition pos, const char *ptr, Sci::Position len);
virtual void Copy() = 0;
virtual void CopyAllowLine();
virtual bool CanPaste();
virtual void Paste() = 0;
void Clear();
virtual void SelectAll();
virtual void Undo();
virtual void Redo();
void DelCharBack(bool allowLineStartDeletion);
virtual void ClaimSelection() = 0;
static int ModifierFlags(bool shift, bool ctrl, bool alt, bool meta=false, bool super=false) noexcept;
virtual void NotifyChange() = 0;
virtual void NotifyFocus(bool focus);
virtual void SetCtrlID(int identifier);
virtual int GetCtrlID() { return ctrlID; }
virtual void NotifyParent(SCNotification scn) = 0;
virtual void NotifyStyleToNeeded(Sci::Position endStyleNeeded);
void NotifyChar(int ch);
void NotifySavePoint(bool isSavePoint);
void NotifyModifyAttempt();
virtual void NotifyDoubleClick(Point pt, int modifiers);
void NotifyHotSpotClicked(Sci::Position position, int modifiers);
void NotifyHotSpotDoubleClicked(Sci::Position position, int modifiers);
void NotifyHotSpotReleaseClick(Sci::Position position, int modifiers);
bool NotifyUpdateUI();
void NotifyPainted();
void NotifyIndicatorClick(bool click, Sci::Position position, int modifiers);
bool NotifyMarginClick(Point pt, int modifiers);
bool NotifyMarginRightClick(Point pt, int modifiers);
void NotifyNeedShown(Sci::Position pos, Sci::Position len);
void NotifyDwelling(Point pt, bool state);
void NotifyZoom();
void NotifyModifyAttempt(Document *document, void *userData) override;
void NotifySavePoint(Document *document, void *userData, bool atSavePoint) override;
void CheckModificationForWrap(DocModification mh);
void NotifyModified(Document *document, DocModification mh, void *userData) override;
void NotifyDeleted(Document *document, void *userData) override;
void NotifyStyleNeeded(Document *doc, void *userData, Sci::Position endStyleNeeded) override;
void NotifyLexerChanged(Document *doc, void *userData) override;
void NotifyErrorOccurred(Document *doc, void *userData, int status) override;
void NotifyMacroRecord(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
void ContainerNeedsUpdate(int flags);
void PageMove(int direction, Selection::selTypes selt=Selection::noSel, bool stuttered = false);
enum { cmSame, cmUpper, cmLower };
virtual std::string CaseMapString(const std::string &s, int caseMapping);
void ChangeCaseOfSelection(int caseMapping);
void LineTranspose();
void LineReverse();
void Duplicate(bool forLine);
virtual void CancelModes();
void NewLine();
SelectionPosition PositionUpOrDown(SelectionPosition spStart, int direction, int lastX);
void CursorUpOrDown(int direction, Selection::selTypes selt);
void ParaUpOrDown(int direction, Selection::selTypes selt);
Range RangeDisplayLine(Sci::Line lineVisible);
Sci::Position StartEndDisplayLine(Sci::Position pos, bool start);
Sci::Position VCHomeDisplayPosition(Sci::Position position);
Sci::Position VCHomeWrapPosition(Sci::Position position);
Sci::Position LineEndWrapPosition(Sci::Position position);
int HorizontalMove(unsigned int iMessage);
int DelWordOrLine(unsigned int iMessage);
virtual int KeyCommand(unsigned int iMessage);
virtual int KeyDefault(int /* key */, int /*modifiers*/);
int KeyDownWithModifiers(int key, int modifiers, bool *consumed);
void Indent(bool forwards);
virtual CaseFolder *CaseFolderForEncoding();
Sci::Position FindText(uptr_t wParam, sptr_t lParam);
void SearchAnchor();
Sci::Position SearchText(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
Sci::Position SearchInTarget(const char *text, Sci::Position length);
void GoToLine(Sci::Line lineNo);
virtual void CopyToClipboard(const SelectionText &selectedText) = 0;
std::string RangeText(Sci::Position start, Sci::Position end) const;
void CopySelectionRange(SelectionText *ss, bool allowLineCopy=false);
void CopyRangeToClipboard(Sci::Position start, Sci::Position end);
void CopyText(size_t length, const char *text);
void SetDragPosition(SelectionPosition newPos);
virtual void DisplayCursor(Window::Cursor c);
virtual bool DragThreshold(Point ptStart, Point ptNow);
virtual void StartDrag();
void DropAt(SelectionPosition position, const char *value, size_t lengthValue, bool moving, bool rectangular);
void DropAt(SelectionPosition position, const char *value, bool moving, bool rectangular);
/** PositionInSelection returns true if position in selection. */
bool PositionInSelection(Sci::Position pos);
bool PointInSelection(Point pt);
bool PointInSelMargin(Point pt) const;
Window::Cursor GetMarginCursor(Point pt) const;
void TrimAndSetSelection(Sci::Position currentPos_, Sci::Position anchor_);
void LineSelection(Sci::Position lineCurrentPos_, Sci::Position lineAnchorPos_, bool wholeLine);
void WordSelection(Sci::Position pos);
void DwellEnd(bool mouseMoved);
void MouseLeave();
virtual void ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers);
virtual void RightButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers);
void ButtonMoveWithModifiers(Point pt, unsigned int curTime, int modifiers);
void ButtonUpWithModifiers(Point pt, unsigned int curTime, int modifiers);
bool Idle();
enum TickReason { tickCaret, tickScroll, tickWiden, tickDwell, tickPlatform };
virtual void TickFor(TickReason reason);
virtual bool FineTickerRunning(TickReason reason);
virtual void FineTickerStart(TickReason reason, int millis, int tolerance);
virtual void FineTickerCancel(TickReason reason);
virtual bool SetIdle(bool) { return false; }
virtual void SetMouseCapture(bool on) = 0;
virtual bool HaveMouseCapture() = 0;
void SetFocusState(bool focusState);
Sci::Position PositionAfterArea(PRectangle rcArea) const;
void StyleToPositionInView(Sci::Position pos);
Sci::Position PositionAfterMaxStyling(Sci::Position posMax, bool scrolling) const;
void StartIdleStyling(bool truncatedLastStyling);
void StyleAreaBounded(PRectangle rcArea, bool scrolling);
void IdleStyling();
virtual void IdleWork();
virtual void QueueIdleWork(WorkNeeded::workItems items, Sci::Position upTo=0);
virtual bool PaintContains(PRectangle rc);
bool PaintContainsMargin();
void CheckForChangeOutsidePaint(Range r);
void SetBraceHighlight(Sci::Position pos0, Sci::Position pos1, int matchStyle);
void SetAnnotationHeights(Sci::Line start, Sci::Line end);
virtual void SetDocPointer(Document *document);
void SetAnnotationVisible(int visible);
Sci::Line ExpandLine(Sci::Line line);
void SetFoldExpanded(Sci::Line lineDoc, bool expanded);
void FoldLine(Sci::Line line, int action);
void FoldExpand(Sci::Line line, int action, int level);
Sci::Line ContractedFoldNext(Sci::Line lineStart) const;
void EnsureLineVisible(Sci::Line lineDoc, bool enforcePolicy);
void FoldChanged(Sci::Line line, int levelNow, int levelPrev);
void NeedShown(Sci::Position pos, Sci::Position len);
void FoldAll(int action);
Sci::Position GetTag(char *tagValue, int tagNumber);
Sci::Position ReplaceTarget(bool replacePatterns, const char *text, Sci::Position length=-1);
bool PositionIsHotspot(Sci::Position position) const;
bool PointIsHotspot(Point pt);
void SetHotSpotRange(const Point *pt);
Range GetHotSpotRange() const override;
void SetHoverIndicatorPosition(Sci::Position position);
void SetHoverIndicatorPoint(Point pt);
int CodePage() const;
virtual bool ValidCodePage(int /* codePage */) const { return true; }
Sci::Line WrapCount(Sci::Line line);
void AddStyledText(const char *buffer, Sci::Position appendLength);
virtual sptr_t DefWndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) = 0;
bool ValidMargin(uptr_t wParam) const;
void StyleSetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
sptr_t StyleGetMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
void SetSelectionNMessage(unsigned int iMessage, uptr_t wParam, sptr_t lParam);
static const char *StringFromEOLMode(int eolMode);
// Coercion functions for transforming WndProc parameters into pointers
static void *PtrFromSPtr(sptr_t lParam) {
return reinterpret_cast<void *>(lParam);
}
static const char *ConstCharPtrFromSPtr(sptr_t lParam) {
return static_cast<const char *>(PtrFromSPtr(lParam));
}
static const unsigned char *ConstUCharPtrFromSPtr(sptr_t lParam) {
return static_cast<const unsigned char *>(PtrFromSPtr(lParam));
}
static char *CharPtrFromSPtr(sptr_t lParam) {
return static_cast<char *>(PtrFromSPtr(lParam));
}
static unsigned char *UCharPtrFromSPtr(sptr_t lParam) {
return static_cast<unsigned char *>(PtrFromSPtr(lParam));
}
static void *PtrFromUPtr(uptr_t wParam) {
return reinterpret_cast<void *>(wParam);
}
static const char *ConstCharPtrFromUPtr(uptr_t wParam) {
return static_cast<const char *>(PtrFromUPtr(wParam));
}
static sptr_t StringResult(sptr_t lParam, const char *val);
static sptr_t BytesResult(sptr_t lParam, const unsigned char *val, size_t len);
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;
// Public so COM methods for drag and drop can set it.
int errorStatus;
friend class AutoSurface;
};
/**
* A smart pointer class to ensure Surfaces are set up and deleted correctly.
*/
class AutoSurface {
private:
std::unique_ptr<Surface> surf;
public:
AutoSurface(Editor *ed, int technology = -1) {
if (ed->wMain.GetID()) {
surf.reset(Surface::Allocate(technology != -1 ? technology : ed->technology));
surf->Init(ed->wMain.GetID());
surf->SetUnicodeMode(SC_CP_UTF8 == ed->CodePage());
surf->SetDBCSMode(ed->CodePage());
}
}
AutoSurface(SurfaceID sid, Editor *ed, int technology = -1) {
if (ed->wMain.GetID()) {
surf.reset(Surface::Allocate(technology != -1 ? technology : ed->technology));
surf->Init(sid, ed->wMain.GetID());
surf->SetUnicodeMode(SC_CP_UTF8 == ed->CodePage());
surf->SetDBCSMode(ed->CodePage());
}
}
// Deleted so AutoSurface objects can not be copied.
AutoSurface(const AutoSurface &) = delete;
AutoSurface(AutoSurface &&) = delete;
void operator=(const AutoSurface &) = delete;
void operator=(AutoSurface &&) = delete;
~AutoSurface() {
}
Surface *operator->() const noexcept {
return surf.get();
}
operator Surface *() const noexcept {
return surf.get();
}
};
}
#endif

View File

@@ -0,0 +1,35 @@
// Scintilla source code edit control
/** @file ElapsedPeriod.h
** Encapsulate C++ <chrono> to simplify use.
**/
// Copyright 2018 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef ELAPSEDPERIOD_H
#define ELAPSEDPERIOD_H
namespace Scintilla {
// Simplified access to high precision timing.
class ElapsedPeriod {
std::chrono::high_resolution_clock::time_point tp;
public:
/// Capture the moment
ElapsedPeriod() : tp(std::chrono::high_resolution_clock::now()) {
}
/// Return duration as floating point seconds
double Duration(bool reset=false) {
const std::chrono::high_resolution_clock::time_point tpNow =
std::chrono::high_resolution_clock::now();
const std::chrono::duration<double> stylingDuration =
std::chrono::duration_cast<std::chrono::duration<double>>(tpNow - tp);
if (reset) {
tp = tpNow;
}
return stylingDuration.count();
}
};
}
#endif

View File

@@ -0,0 +1,135 @@
// Scintilla source code edit control
/** @file ExternalLexer.cxx
** Support external lexers in DLLs or shared libraries.
**/
// 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 <cstdlib>
#include <cassert>
#include <cstring>
#include <stdexcept>
#include <string>
#include <vector>
#include <memory>
#include "Platform.h"
#include "ILexer.h"
#include "Scintilla.h"
#include "SciLexer.h"
#include "LexerModule.h"
#include "Catalogue.h"
#include "ExternalLexer.h"
using namespace Scintilla;
std::unique_ptr<LexerManager> LexerManager::theInstance;
//------------------------------------------
//
// ExternalLexerModule
//
//------------------------------------------
void ExternalLexerModule::SetExternal(GetLexerFactoryFunction fFactory, int index) {
fneFactory = fFactory;
fnFactory = fFactory(index);
}
//------------------------------------------
//
// LexerLibrary
//
//------------------------------------------
LexerLibrary::LexerLibrary(const char *moduleName_) {
// Load the DLL
lib.reset(DynamicLibrary::Load(moduleName_));
if (lib->IsValid()) {
moduleName = 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) {
// Find functions in the DLL
GetLexerNameFn GetLexerName = (GetLexerNameFn)(sptr_t)lib->FindFunction("GetLexerName");
GetLexerFactoryFunction fnFactory = (GetLexerFactoryFunction)(sptr_t)lib->FindFunction("GetLexerFactory");
const int nl = GetLexerCount();
for (int i = 0; i < nl; i++) {
// Assign a buffer for the lexer name.
char lexname[100] = "";
GetLexerName(i, lexname, sizeof(lexname));
ExternalLexerModule *lex = new ExternalLexerModule(SCLEX_AUTOMATIC, nullptr, lexname, nullptr);
// This is storing a second reference to lex in the Catalogue as well as in modules.
// TODO: Should use std::shared_ptr or similar to ensure allocation safety.
Catalogue::AddLexerModule(lex);
// Remember ExternalLexerModule so we don't leak it
modules.push_back(std::unique_ptr<ExternalLexerModule>(lex));
// The external lexer needs to know how to call into its DLL to
// do its lexing and folding, we tell it here.
lex->SetExternal(fnFactory, i);
}
}
}
}
LexerLibrary::~LexerLibrary() {
}
//------------------------------------------
//
// LexerManager
//
//------------------------------------------
/// Return the single LexerManager instance...
LexerManager *LexerManager::GetInstance() {
if (!theInstance)
theInstance.reset(new LexerManager);
return theInstance.get();
}
/// Delete any LexerManager instance...
void LexerManager::DeleteInstance() {
theInstance.reset();
}
/// protected constructor - this is a singleton...
LexerManager::LexerManager() {
}
LexerManager::~LexerManager() {
Clear();
}
void LexerManager::Load(const char *path) {
for (const std::unique_ptr<LexerLibrary> &ll : libraries) {
if (ll->moduleName == path)
return;
}
LexerLibrary *lib = new LexerLibrary(path);
libraries.push_back(std::unique_ptr<LexerLibrary>(lib));
}
void LexerManager::Clear() {
libraries.clear();
}
//------------------------------------------
//
// LMMinder -- trigger to clean up at exit.
//
//------------------------------------------
LMMinder::~LMMinder() {
LexerManager::DeleteInstance();
}
LMMinder minder;

View File

@@ -0,0 +1,80 @@
// Scintilla source code edit control
/** @file ExternalLexer.h
** Support external lexers in DLLs or shared libraries.
**/
// 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_QT
#include <qglobal.h>
#if defined(Q_OS_WIN32) || defined(Q_OS_WIN64)
#define EXT_LEXER_DECL __stdcall
#else
#define EXT_LEXER_DECL
#endif
#else
#define EXT_LEXER_DECL
#endif
namespace Scintilla {
typedef int (EXT_LEXER_DECL *GetLexerCountFn)();
typedef void (EXT_LEXER_DECL *GetLexerNameFn)(unsigned int Index, char *name, int buflength);
typedef LexerFactoryFunction(EXT_LEXER_DECL *GetLexerFactoryFunction)(unsigned int Index);
/// Sub-class of LexerModule to use an external lexer.
class ExternalLexerModule : public LexerModule {
protected:
GetLexerFactoryFunction fneFactory;
std::string name;
public:
ExternalLexerModule(int language_, LexerFunction fnLexer_,
const char *languageName_=nullptr, LexerFunction fnFolder_=nullptr) :
LexerModule(language_, fnLexer_, nullptr, fnFolder_),
fneFactory(nullptr), name(languageName_){
languageName = name.c_str();
}
virtual void SetExternal(GetLexerFactoryFunction fFactory, int index);
};
/// LexerLibrary exists for every External Lexer DLL, contains ExternalLexerModules.
class LexerLibrary {
std::unique_ptr<DynamicLibrary> lib;
std::vector<std::unique_ptr<ExternalLexerModule>> modules;
public:
explicit LexerLibrary(const char *moduleName_);
~LexerLibrary();
std::string moduleName;
};
/// 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 std::unique_ptr<LexerManager> theInstance;
std::vector<std::unique_ptr<LexerLibrary>> libraries;
};
class LMMinder {
public:
~LMMinder();
};
}
#endif

View File

@@ -0,0 +1,27 @@
// Scintilla source code edit control
/** @file FontQuality.h
** Definitions to control font anti-aliasing.
** Redefine constants from Scintilla.h to avoid including Scintilla.h in PlatWin.cxx.
**/
// Copyright 1998-2009 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef FONTQUALITY_H
#define FONTQUALITY_H
namespace Scintilla {
// These definitions match Scintilla.h
#define SC_EFF_QUALITY_MASK 0xF
#define SC_EFF_QUALITY_DEFAULT 0
#define SC_EFF_QUALITY_NON_ANTIALIASED 1
#define SC_EFF_QUALITY_ANTIALIASED 2
#define SC_EFF_QUALITY_LCD_OPTIMIZED 3
// These definitions must match SC_TECHNOLOGY_* in Scintilla.h
#define SCWIN_TECH_GDI 0
#define SCWIN_TECH_DIRECTWRITE 1
}
#endif

View File

@@ -0,0 +1,223 @@
// 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 <cmath>
#include <stdexcept>
#include <vector>
#include <map>
#include <algorithm>
#include <memory>
#include "Platform.h"
#include "Scintilla.h"
#include "StringCopy.h"
#include "IntegerRectangle.h"
#include "Indicator.h"
#include "XPM.h"
using namespace Scintilla;
static PRectangle PixelGridAlign(const PRectangle &rc) {
// Move left and right side to nearest pixel to avoid blurry visuals
return PRectangle(round(rc.left), floor(rc.top),
round(rc.right), floor(rc.bottom));
}
void Indicator::Draw(Surface *surface, const PRectangle &rc, const PRectangle &rcLine, const PRectangle &rcCharacter, DrawState drawState, int value) const {
StyleAndColour sacDraw = sacNormal;
if (Flags() & SC_INDICFLAG_VALUEFORE) {
sacDraw.fore = ColourDesired(value & SC_INDICVALUEMASK);
}
if (drawState == drawHover) {
sacDraw = sacHover;
}
const IntegerRectangle irc(rc);
surface->PenColour(sacDraw.fore);
const int ymid = (irc.bottom + irc.top) / 2;
if (sacDraw.style == INDIC_SQUIGGLE) {
const IntegerRectangle ircSquiggle(PixelGridAlign(rc));
int x = ircSquiggle.left;
const int xLast = ircSquiggle.right;
int y = 0;
surface->MoveTo(x, irc.top + y);
while (x < xLast) {
if ((x + 2) > xLast) {
y = 1;
x = xLast;
} else {
x += 2;
y = 2 - y;
}
surface->LineTo(x, irc.top + y);
}
} else if (sacDraw.style == INDIC_SQUIGGLEPIXMAP) {
const PRectangle rcSquiggle = PixelGridAlign(rc);
const int width = std::min(4000, static_cast<int>(rcSquiggle.Width()));
RGBAImage image(width, 3, 1.0, nullptr);
enum { alphaFull = 0xff, alphaSide = 0x2f, alphaSide2=0x5f };
for (int x = 0; x < width; x++) {
if (x%2) {
// Two halfway columns have a full pixel in middle flanked by light pixels
image.SetPixel(x, 0, sacDraw.fore, alphaSide);
image.SetPixel(x, 1, sacDraw.fore, alphaFull);
image.SetPixel(x, 2, sacDraw.fore, alphaSide);
} else {
// Extreme columns have a full pixel at bottom or top and a mid-tone pixel in centre
image.SetPixel(x, (x % 4) ? 0 : 2, sacDraw.fore, alphaFull);
image.SetPixel(x, 1, sacDraw.fore, alphaSide2);
}
}
surface->DrawRGBAImage(rcSquiggle, image.GetWidth(), image.GetHeight(), image.Pixels());
} else if (sacDraw.style == INDIC_SQUIGGLELOW) {
surface->MoveTo(irc.left, irc.top);
int x = irc.left + 3;
int y = 0;
while (x < rc.right) {
surface->LineTo(x - 1, irc.top + y);
y = 1 - y;
surface->LineTo(x, irc.top + y);
x += 3;
}
surface->LineTo(irc.right, irc.top + y); // Finish the line
} else if (sacDraw.style == INDIC_TT) {
surface->MoveTo(irc.left, ymid);
int x = irc.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(irc.right, ymid); // Finish the line
if (x - 3 <= rc.right) {
surface->MoveTo(x-3, ymid);
surface->LineTo(x-3, ymid+2);
}
} else if (sacDraw.style == INDIC_DIAGONAL) {
int x = irc.left;
while (x < rc.right) {
surface->MoveTo(x, irc.top + 2);
int endX = x+3;
int endY = irc.top - 1;
if (endX > rc.right) {
endY += endX - irc.right;
endX = irc.right;
}
surface->LineTo(endX, endY);
x += 4;
}
} else if (sacDraw.style == INDIC_STRIKE) {
surface->MoveTo(irc.left, irc.top - 4);
surface->LineTo(irc.right, irc.top - 4);
} else if ((sacDraw.style == INDIC_HIDDEN) || (sacDraw.style == INDIC_TEXTFORE)) {
// Draw nothing
} else if (sacDraw.style == INDIC_BOX) {
surface->MoveTo(irc.left, ymid + 1);
surface->LineTo(irc.right, ymid + 1);
const int lineTop = static_cast<int>(rcLine.top) + 1;
surface->LineTo(irc.right, lineTop);
surface->LineTo(irc.left, lineTop);
surface->LineTo(irc.left, ymid + 1);
} else if (sacDraw.style == INDIC_ROUNDBOX ||
sacDraw.style == INDIC_STRAIGHTBOX ||
sacDraw.style == INDIC_FULLBOX) {
PRectangle rcBox = rcLine;
if (sacDraw.style != INDIC_FULLBOX)
rcBox.top = rcLine.top + 1;
rcBox.left = rc.left;
rcBox.right = rc.right;
surface->AlphaRectangle(rcBox, (sacDraw.style == INDIC_ROUNDBOX) ? 1 : 0,
sacDraw.fore, fillAlpha, sacDraw.fore, outlineAlpha, 0);
} else if (sacDraw.style == INDIC_GRADIENT ||
sacDraw.style == INDIC_GRADIENTCENTRE) {
PRectangle rcBox = rc;
rcBox.top = rcLine.top + 1;
rcBox.bottom = rcLine.bottom;
const Surface::GradientOptions options = Surface::GradientOptions::topToBottom;
const ColourAlpha start(sacNormal.fore, fillAlpha);
const ColourAlpha end(sacNormal.fore, 0);
std::vector<ColourStop> stops;
switch (sacDraw.style) {
case INDIC_GRADIENT:
stops.push_back(ColourStop(0.0, start));
stops.push_back(ColourStop(1.0, end));
break;
case INDIC_GRADIENTCENTRE:
stops.push_back(ColourStop(0.0, end));
stops.push_back(ColourStop(0.5, start));
stops.push_back(ColourStop(1.0, end));
break;
}
surface->GradientRectangle(rcBox, stops, options);
} else if (sacDraw.style == INDIC_DOTBOX) {
PRectangle rcBox = PixelGridAlign(rc);
rcBox.top = rcLine.top + 1;
rcBox.bottom = rcLine.bottom;
IntegerRectangle ircBox(rcBox);
// Cap width at 4000 to avoid large allocations when mistakes made
const int width = std::min(ircBox.Width(), 4000);
RGBAImage image(width, ircBox.Height(), 1.0, nullptr);
// Draw horizontal lines top and bottom
for (int x=0; x<width; x++) {
for (int y = 0; y<ircBox.Height(); y += ircBox.Height() - 1) {
image.SetPixel(x, y, sacDraw.fore, ((x + y) % 2) ? outlineAlpha : fillAlpha);
}
}
// Draw vertical lines left and right
for (int y = 1; y<ircBox.Height(); y++) {
for (int x=0; x<width; x += width-1) {
image.SetPixel(x, y, sacDraw.fore, ((x + y) % 2) ? outlineAlpha : fillAlpha);
}
}
surface->DrawRGBAImage(rcBox, image.GetWidth(), image.GetHeight(), image.Pixels());
} else if (sacDraw.style == INDIC_DASH) {
int x = irc.left;
while (x < rc.right) {
surface->MoveTo(x, ymid);
surface->LineTo(std::min(x + 4, irc.right), ymid);
x += 7;
}
} else if (sacDraw.style == INDIC_DOTS) {
int x = irc.left;
while (x < irc.right) {
const PRectangle rcDot = PRectangle::FromInts(x, ymid, x + 1, ymid + 1);
surface->FillRectangle(rcDot, sacDraw.fore);
x += 2;
}
} else if (sacDraw.style == INDIC_COMPOSITIONTHICK) {
const PRectangle rcComposition(rc.left+1, rcLine.bottom-2, rc.right-1, rcLine.bottom);
surface->FillRectangle(rcComposition, sacDraw.fore);
} else if (sacDraw.style == INDIC_COMPOSITIONTHIN) {
const PRectangle rcComposition(rc.left+1, rcLine.bottom-2, rc.right-1, rcLine.bottom-1);
surface->FillRectangle(rcComposition, sacDraw.fore);
} else if (sacDraw.style == INDIC_POINT || sacDraw.style == INDIC_POINTCHARACTER) {
if (rcCharacter.Width() >= 0.1) {
const XYPOSITION pixelHeight = floor(rc.Height() - 1.0f); // 1 pixel onto next line if multiphase
const XYPOSITION x = (sacDraw.style == INDIC_POINT) ? (rcCharacter.left) : ((rcCharacter.right + rcCharacter.left) / 2);
const XYPOSITION ix = round(x);
const XYPOSITION iy = floor(rc.top + 1.0f);
Point pts[] = {
Point(ix - pixelHeight, iy + pixelHeight), // Left
Point(ix + pixelHeight, iy + pixelHeight), // Right
Point(ix, iy) // Top
};
surface->Polygon(pts, ELEMENTS(pts), sacDraw.fore, sacDraw.fore);
}
} else { // Either INDIC_PLAIN or unknown
surface->MoveTo(irc.left, ymid);
surface->LineTo(irc.right, ymid);
}
}
void Indicator::SetFlags(int attributes_) {
attributes = attributes_;
}

View File

@@ -0,0 +1,56 @@
// 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
namespace Scintilla {
struct StyleAndColour {
int style;
ColourDesired fore;
StyleAndColour() : style(INDIC_PLAIN), fore(0, 0, 0) {
}
StyleAndColour(int style_, ColourDesired fore_ = ColourDesired(0, 0, 0)) : style(style_), fore(fore_) {
}
bool operator==(const StyleAndColour &other) const {
return (style == other.style) && (fore == other.fore);
}
};
/**
*/
class Indicator {
public:
enum DrawState { drawNormal, drawHover };
StyleAndColour sacNormal;
StyleAndColour sacHover;
bool under;
int fillAlpha;
int outlineAlpha;
int attributes;
Indicator() : under(false), fillAlpha(30), outlineAlpha(50), attributes(0) {
}
Indicator(int style_, ColourDesired fore_=ColourDesired(0,0,0), bool under_=false, int fillAlpha_=30, int outlineAlpha_=50) :
sacNormal(style_, fore_), sacHover(style_, fore_), under(under_), fillAlpha(fillAlpha_), outlineAlpha(outlineAlpha_), attributes(0) {
}
void Draw(Surface *surface, const PRectangle &rc, const PRectangle &rcLine, const PRectangle &rcCharacter, DrawState drawState, int value) const;
bool IsDynamic() const {
return !(sacNormal == sacHover);
}
bool OverridesTextFore() const {
return sacNormal.style == INDIC_TEXTFORE || sacHover.style == INDIC_TEXTFORE;
}
int Flags() const {
return attributes;
}
void SetFlags(int attributes_);
};
}
#endif

View File

@@ -0,0 +1,29 @@
// Scintilla source code edit control
/** @file IntegerRectangle.h
** A rectangle with integer coordinates.
**/
// Copyright 2018 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef INTEGERRECTANGLE_H
#define INTEGERRECTANGLE_H
namespace Scintilla {
struct IntegerRectangle {
int left;
int top;
int right;
int bottom;
explicit IntegerRectangle(PRectangle rc) noexcept :
left(static_cast<int>(rc.left)), top(static_cast<int>(rc.top)),
right(static_cast<int>(rc.right)), bottom(static_cast<int>(rc.bottom)) {
}
int Width() const noexcept { return right - left; }
int Height() const noexcept { return bottom - top; }
};
}
#endif

View File

@@ -0,0 +1,164 @@
// 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 <cstdlib>
#include <stdexcept>
#include <vector>
#include <map>
#include <memory>
#include "Platform.h"
#include "Scintilla.h"
#include "KeyMap.h"
using namespace Scintilla;
KeyMap::KeyMap() {
for (int i = 0; MapDefault[i].key; i++) {
AssignCmdKey(MapDefault[i].key,
MapDefault[i].modifiers,
MapDefault[i].msg);
}
}
KeyMap::~KeyMap() {
Clear();
}
void KeyMap::Clear() {
kmap.clear();
}
void KeyMap::AssignCmdKey(int key, int modifiers, unsigned int msg) {
kmap[KeyModifiers(key, modifiers)] = msg;
}
unsigned int KeyMap::Find(int key, int modifiers) const {
std::map<KeyModifiers, unsigned int>::const_iterator it = kmap.find(KeyModifiers(key, modifiers));
return (it == kmap.end()) ? 0 : it->second;
}
const std::map<KeyModifiers, unsigned int> &KeyMap::GetKeyMap() const {
return kmap;
}
#if PLAT_GTK_MACOSX
#define OS_X_KEYS 1
#else
#define OS_X_KEYS 0
#endif
// Define a modifier that is exactly Ctrl key on all platforms
// Most uses of Ctrl map to Cmd on OS X but some can't so use SCI_[S]CTRL_META
#if OS_X_KEYS
#define SCI_CTRL_META SCI_META
#define SCI_SCTRL_META (SCI_META | SCI_SHIFT)
#else
#define SCI_CTRL_META SCI_CTRL
#define SCI_SCTRL_META (SCI_CTRL | SCI_SHIFT)
#endif
const KeyToCommand KeyMap::MapDefault[] = {
#if OS_X_KEYS
{SCK_DOWN, SCI_CTRL, SCI_DOCUMENTEND},
{SCK_DOWN, SCI_CSHIFT, SCI_DOCUMENTENDEXTEND},
{SCK_UP, SCI_CTRL, SCI_DOCUMENTSTART},
{SCK_UP, SCI_CSHIFT, SCI_DOCUMENTSTARTEXTEND},
{SCK_LEFT, SCI_CTRL, SCI_VCHOME},
{SCK_LEFT, SCI_CSHIFT, SCI_VCHOMEEXTEND},
{SCK_RIGHT, SCI_CTRL, SCI_LINEEND},
{SCK_RIGHT, SCI_CSHIFT, SCI_LINEENDEXTEND},
#endif
{SCK_DOWN, SCI_NORM, SCI_LINEDOWN},
{SCK_DOWN, SCI_SHIFT, SCI_LINEDOWNEXTEND},
{SCK_DOWN, SCI_CTRL_META, SCI_LINESCROLLDOWN},
{SCK_DOWN, SCI_ASHIFT, SCI_LINEDOWNRECTEXTEND},
{SCK_UP, SCI_NORM, SCI_LINEUP},
{SCK_UP, SCI_SHIFT, SCI_LINEUPEXTEND},
{SCK_UP, SCI_CTRL_META, 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_META, SCI_WORDLEFT},
{SCK_LEFT, SCI_SCTRL_META, SCI_WORDLEFTEXTEND},
{SCK_LEFT, SCI_ASHIFT, SCI_CHARLEFTRECTEXTEND},
{SCK_RIGHT, SCI_NORM, SCI_CHARRIGHT},
{SCK_RIGHT, SCI_SHIFT, SCI_CHARRIGHTEXTEND},
{SCK_RIGHT, SCI_CTRL_META, SCI_WORDRIGHT},
{SCK_RIGHT, SCI_SCTRL_META, 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_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_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},
#if OS_X_KEYS
{'Z', SCI_CSHIFT, SCI_REDO},
#else
{'Y', SCI_CTRL, SCI_REDO},
#endif
{'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_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},
};

View File

@@ -0,0 +1,64 @@
// 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 KEYMAP_H
#define KEYMAP_H
namespace Scintilla {
#define SCI_NORM 0
#define SCI_SHIFT SCMOD_SHIFT
#define SCI_CTRL SCMOD_CTRL
#define SCI_ALT SCMOD_ALT
#define SCI_META SCMOD_META
#define SCI_SUPER SCMOD_SUPER
#define SCI_CSHIFT (SCI_CTRL | SCI_SHIFT)
#define SCI_ASHIFT (SCI_ALT | SCI_SHIFT)
/**
*/
class KeyModifiers {
public:
int key;
int modifiers;
KeyModifiers(int key_, int modifiers_) : key(key_), modifiers(modifiers_) {
}
bool operator<(const KeyModifiers &other) const {
if (key == other.key)
return modifiers < other.modifiers;
else
return key < other.key;
}
};
/**
*/
class KeyToCommand {
public:
int key;
int modifiers;
unsigned int msg;
};
/**
*/
class KeyMap {
std::map<KeyModifiers, unsigned int> kmap;
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) const; // 0 returned on failure
const std::map<KeyModifiers, unsigned int> &GetKeyMap() const;
};
}
#endif

View File

@@ -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.

View File

@@ -0,0 +1,436 @@
// Scintilla source code edit control
/** @file LineMarker.cxx
** Defines the look of a line marker in the margin.
**/
// Copyright 1998-2011 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstring>
#include <cmath>
#include <stdexcept>
#include <vector>
#include <map>
#include <algorithm>
#include <memory>
#include "Platform.h"
#include "Scintilla.h"
#include "StringCopy.h"
#include "IntegerRectangle.h"
#include "XPM.h"
#include "LineMarker.h"
using namespace Scintilla;
LineMarker::~LineMarker() {
}
LineMarker::LineMarker() {
markType = SC_MARK_CIRCLE;
fore = ColourDesired(0, 0, 0);
back = ColourDesired(0xff, 0xff, 0xff);
backSelected = ColourDesired(0xff, 0x00, 0x00);
alpha = SC_ALPHA_NOALPHA;
customDraw = nullptr;
}
LineMarker::LineMarker(const LineMarker &) {
// Defined to avoid pxpm and image being blindly copied, not as a complete copy constructor.
markType = SC_MARK_CIRCLE;
fore = ColourDesired(0, 0, 0);
back = ColourDesired(0xff, 0xff, 0xff);
backSelected = ColourDesired(0xff, 0x00, 0x00);
alpha = SC_ALPHA_NOALPHA;
pxpm.reset();
image.reset();
customDraw = nullptr;
}
LineMarker &LineMarker::operator=(const LineMarker &other) {
// Defined to avoid pxpm and image being blindly copied, not as a complete assignment operator.
if (this != &other) {
markType = SC_MARK_CIRCLE;
fore = ColourDesired(0, 0, 0);
back = ColourDesired(0xff, 0xff, 0xff);
backSelected = ColourDesired(0xff, 0x00, 0x00);
alpha = SC_ALPHA_NOALPHA;
pxpm.reset();
image.reset();
customDraw = nullptr;
}
return *this;
}
void LineMarker::SetXPM(const char *textForm) {
pxpm.reset(new XPM(textForm));
markType = SC_MARK_PIXMAP;
}
void LineMarker::SetXPM(const char *const *linesForm) {
pxpm.reset(new XPM(linesForm));
markType = SC_MARK_PIXMAP;
}
void LineMarker::SetRGBAImage(Point sizeRGBAImage, float scale, const unsigned char *pixelsRGBAImage) {
image.reset(new RGBAImage(static_cast<int>(sizeRGBAImage.x), static_cast<int>(sizeRGBAImage.y), scale, pixelsRGBAImage));
markType = SC_MARK_RGBAIMAGE;
}
static void DrawBox(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore, ColourDesired back) {
const PRectangle rc = PRectangle::FromInts(
centreX - armSize,
centreY - armSize,
centreX + armSize + 1,
centreY + armSize + 1);
surface->RectangleDraw(rc, back, fore);
}
static void DrawCircle(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore, ColourDesired back) {
const PRectangle rcCircle = PRectangle::FromInts(
centreX - armSize,
centreY - armSize,
centreX + armSize + 1,
centreY + armSize + 1);
surface->Ellipse(rcCircle, back, fore);
}
static void DrawPlus(Surface *surface, int centreX, int centreY, int armSize, ColourDesired fore) {
const PRectangle rcV = PRectangle::FromInts(centreX, centreY - armSize + 2, centreX + 1, centreY + armSize - 2 + 1);
surface->FillRectangle(rcV, fore);
const PRectangle rcH = PRectangle::FromInts(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, ColourDesired fore) {
const PRectangle rcH = PRectangle::FromInts(centreX - armSize + 2, centreY, centreX + armSize - 2 + 1, centreY + 1);
surface->FillRectangle(rcH, fore);
}
void LineMarker::Draw(Surface *surface, PRectangle &rcWhole, Font &fontForCharacter, typeOfFold tFold, int marginStyle) const {
if (customDraw) {
customDraw(surface, rcWhole, fontForCharacter, tFold, marginStyle, this);
return;
}
ColourDesired colourHead = back;
ColourDesired colourBody = back;
ColourDesired colourTail = back;
switch (tFold) {
case LineMarker::head :
case LineMarker::headWithTail :
colourHead = backSelected;
colourTail = backSelected;
break;
case LineMarker::body :
colourHead = backSelected;
colourBody = backSelected;
break;
case LineMarker::tail :
colourBody = backSelected;
colourTail = backSelected;
break;
default :
// LineMarker::undefined
break;
}
if ((markType == SC_MARK_PIXMAP) && (pxpm)) {
pxpm->Draw(surface, rcWhole);
return;
}
if ((markType == SC_MARK_RGBAIMAGE) && (image)) {
// Make rectangle just large enough to fit image centred on centre of rcWhole
PRectangle rcImage;
rcImage.top = ((rcWhole.top + rcWhole.bottom) - image->GetScaledHeight()) / 2;
rcImage.bottom = rcImage.top + image->GetScaledHeight();
rcImage.left = ((rcWhole.left + rcWhole.right) - image->GetScaledWidth()) / 2;
rcImage.right = rcImage.left + image->GetScaledWidth();
surface->DrawRGBAImage(rcImage, image->GetWidth(), image->GetHeight(), image->Pixels());
return;
}
const IntegerRectangle ircWhole(rcWhole);
// Restrict most shapes a bit
const PRectangle rc(rcWhole.left, rcWhole.top + 1, rcWhole.right, rcWhole.bottom - 1);
// Ensure does not go beyond edge
const int minDim = std::min(ircWhole.Width(), ircWhole.Height()-2) - 1;
int centreX = (ircWhole.right + ircWhole.left) / 2;
const int centreY = (ircWhole.bottom + ircWhole.top) / 2;
const int dimOn2 = minDim / 2;
const int dimOn4 = minDim / 4;
const int blobSize = dimOn2-1;
const int armSize = dimOn2-2;
if (marginStyle == SC_MARGIN_NUMBER || marginStyle == SC_MARGIN_TEXT || marginStyle == SC_MARGIN_RTEXT) {
// On textual margins move marker to the left to try to avoid overlapping the text
centreX = ircWhole.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, back);
} else if (markType == SC_MARK_CIRCLE) {
const PRectangle rcCircle = PRectangle::FromInts(
centreX - dimOn2,
centreY - dimOn2,
centreX + dimOn2,
centreY + dimOn2);
surface->Ellipse(rcCircle, fore, back);
} else if (markType == SC_MARK_ARROW) {
Point pts[] = {
Point::FromInts(centreX - dimOn4, centreY - dimOn2),
Point::FromInts(centreX - dimOn4, centreY + dimOn2),
Point::FromInts(centreX + dimOn2 - dimOn4, centreY),
};
surface->Polygon(pts, ELEMENTS(pts), fore, back);
} else if (markType == SC_MARK_ARROWDOWN) {
Point pts[] = {
Point::FromInts(centreX - dimOn2, centreY - dimOn4),
Point::FromInts(centreX + dimOn2, centreY - dimOn4),
Point::FromInts(centreX, centreY + dimOn2 - dimOn4),
};
surface->Polygon(pts, ELEMENTS(pts), fore, back);
} else if (markType == SC_MARK_PLUS) {
Point pts[] = {
Point::FromInts(centreX - armSize, centreY - 1),
Point::FromInts(centreX - 1, centreY - 1),
Point::FromInts(centreX - 1, centreY - armSize),
Point::FromInts(centreX + 1, centreY - armSize),
Point::FromInts(centreX + 1, centreY - 1),
Point::FromInts(centreX + armSize, centreY -1),
Point::FromInts(centreX + armSize, centreY +1),
Point::FromInts(centreX + 1, centreY + 1),
Point::FromInts(centreX + 1, centreY + armSize),
Point::FromInts(centreX - 1, centreY + armSize),
Point::FromInts(centreX - 1, centreY + 1),
Point::FromInts(centreX - armSize, centreY + 1),
};
surface->Polygon(pts, ELEMENTS(pts), fore, back);
} else if (markType == SC_MARK_MINUS) {
Point pts[] = {
Point::FromInts(centreX - armSize, centreY - 1),
Point::FromInts(centreX + armSize, centreY -1),
Point::FromInts(centreX + armSize, centreY +1),
Point::FromInts(centreX - armSize, centreY + 1),
};
surface->Polygon(pts, ELEMENTS(pts), fore, back);
} 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, back);
} else if (markType == SC_MARK_EMPTY || markType == SC_MARK_BACKGROUND ||
markType == SC_MARK_UNDERLINE || markType == SC_MARK_AVAILABLE) {
// An invisible marker so don't draw anything
} else if (markType == SC_MARK_VLINE) {
surface->PenColour(colourBody);
surface->MoveTo(centreX, ircWhole.top);
surface->LineTo(centreX, ircWhole.bottom);
} else if (markType == SC_MARK_LCORNER) {
surface->PenColour(colourTail);
surface->MoveTo(centreX, ircWhole.top);
surface->LineTo(centreX, centreY);
surface->LineTo(ircWhole.right - 1, centreY);
} else if (markType == SC_MARK_TCORNER) {
surface->PenColour(colourTail);
surface->MoveTo(centreX, centreY);
surface->LineTo(ircWhole.right - 1, centreY);
surface->PenColour(colourBody);
surface->MoveTo(centreX, ircWhole.top);
surface->LineTo(centreX, centreY + 1);
surface->PenColour(colourHead);
surface->LineTo(centreX, ircWhole.bottom);
} else if (markType == SC_MARK_LCORNERCURVE) {
surface->PenColour(colourTail);
surface->MoveTo(centreX, ircWhole.top);
surface->LineTo(centreX, centreY-3);
surface->LineTo(centreX+3, centreY);
surface->LineTo(ircWhole.right - 1, centreY);
} else if (markType == SC_MARK_TCORNERCURVE) {
surface->PenColour(colourTail);
surface->MoveTo(centreX, centreY-3);
surface->LineTo(centreX+3, centreY);
surface->LineTo(ircWhole.right - 1, centreY);
surface->PenColour(colourBody);
surface->MoveTo(centreX, ircWhole.top);
surface->LineTo(centreX, centreY-2);
surface->PenColour(colourHead);
surface->LineTo(centreX, ircWhole.bottom);
} else if (markType == SC_MARK_BOXPLUS) {
DrawBox(surface, centreX, centreY, blobSize, fore, colourHead);
DrawPlus(surface, centreX, centreY, blobSize, colourTail);
} else if (markType == SC_MARK_BOXPLUSCONNECTED) {
if (tFold == LineMarker::headWithTail)
surface->PenColour(colourTail);
else
surface->PenColour(colourBody);
surface->MoveTo(centreX, centreY + blobSize);
surface->LineTo(centreX, ircWhole.bottom);
surface->PenColour(colourBody);
surface->MoveTo(centreX, ircWhole.top);
surface->LineTo(centreX, centreY - blobSize);
DrawBox(surface, centreX, centreY, blobSize, fore, colourHead);
DrawPlus(surface, centreX, centreY, blobSize, colourTail);
if (tFold == LineMarker::body) {
surface->PenColour(colourTail);
surface->MoveTo(centreX + 1, centreY + blobSize);
surface->LineTo(centreX + blobSize + 1, centreY + blobSize);
surface->MoveTo(centreX + blobSize, centreY + blobSize);
surface->LineTo(centreX + blobSize, centreY - blobSize);
surface->MoveTo(centreX + 1, centreY - blobSize);
surface->LineTo(centreX + blobSize + 1, centreY - blobSize);
}
} else if (markType == SC_MARK_BOXMINUS) {
DrawBox(surface, centreX, centreY, blobSize, fore, colourHead);
DrawMinus(surface, centreX, centreY, blobSize, colourTail);
surface->PenColour(colourHead);
surface->MoveTo(centreX, centreY + blobSize);
surface->LineTo(centreX, ircWhole.bottom);
} else if (markType == SC_MARK_BOXMINUSCONNECTED) {
DrawBox(surface, centreX, centreY, blobSize, fore, colourHead);
DrawMinus(surface, centreX, centreY, blobSize, colourTail);
surface->PenColour(colourHead);
surface->MoveTo(centreX, centreY + blobSize);
surface->LineTo(centreX, ircWhole.bottom);
surface->PenColour(colourBody);
surface->MoveTo(centreX, ircWhole.top);
surface->LineTo(centreX, centreY - blobSize);
if (tFold == LineMarker::body) {
surface->PenColour(colourTail);
surface->MoveTo(centreX + 1, centreY + blobSize);
surface->LineTo(centreX + blobSize + 1, centreY + blobSize);
surface->MoveTo(centreX + blobSize, centreY + blobSize);
surface->LineTo(centreX + blobSize, centreY - blobSize);
surface->MoveTo(centreX + 1, centreY - blobSize);
surface->LineTo(centreX + blobSize + 1, centreY - blobSize);
}
} else if (markType == SC_MARK_CIRCLEPLUS) {
DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead);
DrawPlus(surface, centreX, centreY, blobSize, colourTail);
} else if (markType == SC_MARK_CIRCLEPLUSCONNECTED) {
if (tFold == LineMarker::headWithTail)
surface->PenColour(colourTail);
else
surface->PenColour(colourBody);
surface->MoveTo(centreX, centreY + blobSize);
surface->LineTo(centreX, ircWhole.bottom);
surface->PenColour(colourBody);
surface->MoveTo(centreX, ircWhole.top);
surface->LineTo(centreX, centreY - blobSize);
DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead);
DrawPlus(surface, centreX, centreY, blobSize, colourTail);
} else if (markType == SC_MARK_CIRCLEMINUS) {
surface->PenColour(colourHead);
surface->MoveTo(centreX, centreY + blobSize);
surface->LineTo(centreX, ircWhole.bottom);
DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead);
DrawMinus(surface, centreX, centreY, blobSize, colourTail);
} else if (markType == SC_MARK_CIRCLEMINUSCONNECTED) {
surface->PenColour(colourHead);
surface->MoveTo(centreX, centreY + blobSize);
surface->LineTo(centreX, ircWhole.bottom);
surface->PenColour(colourBody);
surface->MoveTo(centreX, ircWhole.top);
surface->LineTo(centreX, centreY - blobSize);
DrawCircle(surface, centreX, centreY, blobSize, fore, colourHead);
DrawMinus(surface, centreX, centreY, blobSize, colourTail);
} else if (markType >= SC_MARK_CHARACTER) {
char character[1];
character[0] = static_cast<char>(markType - SC_MARK_CHARACTER);
const XYPOSITION width = surface->WidthText(fontForCharacter, character, 1);
PRectangle rcText = rc;
rcText.left += (rc.Width() - width) / 2;
rcText.right = rc.left + width;
surface->DrawTextClipped(rcText, fontForCharacter, rcText.bottom - 2,
character, 1, fore, back);
} else if (markType == SC_MARK_DOTDOTDOT) {
XYPOSITION right = static_cast<XYPOSITION>(centreX - 6);
for (int b=0; b<3; b++) {
const PRectangle rcBlob(right, rc.bottom - 4, right + 2, rc.bottom-2);
surface->FillRectangle(rcBlob, fore);
right += 5.0f;
}
} else if (markType == SC_MARK_ARROWS) {
surface->PenColour(fore);
int right = centreX - 2;
const int armLength = dimOn2 - 1;
for (int b = 0; b<3; b++) {
surface->MoveTo(right, centreY);
surface->LineTo(right - armLength, centreY - armLength);
surface->MoveTo(right, centreY);
surface->LineTo(right - armLength, centreY + armLength);
right += 4;
}
} else if (markType == SC_MARK_SHORTARROW) {
Point pts[] = {
Point::FromInts(centreX, centreY + dimOn2),
Point::FromInts(centreX + dimOn2, centreY),
Point::FromInts(centreX, centreY - dimOn2),
Point::FromInts(centreX, centreY - dimOn4),
Point::FromInts(centreX - dimOn4, centreY - dimOn4),
Point::FromInts(centreX - dimOn4, centreY + dimOn4),
Point::FromInts(centreX, centreY + dimOn4),
Point::FromInts(centreX, centreY + dimOn2),
};
surface->Polygon(pts, ELEMENTS(pts), fore, back);
} else if (markType == SC_MARK_LEFTRECT) {
PRectangle rcLeft = rcWhole;
rcLeft.right = rcLeft.left + 4;
surface->FillRectangle(rcLeft, back);
} else if (markType == SC_MARK_BOOKMARK) {
const int halfHeight = minDim / 3;
Point pts[] = {
Point::FromInts(ircWhole.left, centreY-halfHeight),
Point::FromInts(ircWhole.right - 3, centreY - halfHeight),
Point::FromInts(ircWhole.right - 3 - halfHeight, centreY),
Point::FromInts(ircWhole.right - 3, centreY + halfHeight),
Point::FromInts(ircWhole.left, centreY + halfHeight),
};
surface->Polygon(pts, ELEMENTS(pts), fore, back);
} else { // SC_MARK_FULLRECT
surface->FillRectangle(rcWhole, back);
}
}

View File

@@ -0,0 +1,48 @@
// Scintilla source code edit control
/** @file LineMarker.h
** Defines the look of a line marker in the margin .
**/
// Copyright 1998-2011 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
namespace Scintilla {
class XPM;
class RGBAImage;
typedef void (*DrawLineMarkerFn)(Surface *surface, PRectangle &rcWhole, Font &fontForCharacter, int tFold, int marginStyle, const void *lineMarker);
/**
*/
class LineMarker {
public:
enum typeOfFold { undefined, head, body, tail, headWithTail };
int markType;
ColourDesired fore;
ColourDesired back;
ColourDesired backSelected;
int alpha;
std::unique_ptr<XPM> pxpm;
std::unique_ptr<RGBAImage> image;
/** Some platforms, notably PLAT_CURSES, do not support Scintilla's native
* Draw function for drawing line markers. Allow those platforms to override
* it instead of creating a new method(s) in the Surface class that existing
* platforms must implement as empty. */
DrawLineMarkerFn customDraw;
LineMarker();
LineMarker(const LineMarker &);
virtual ~LineMarker();
LineMarker &operator=(const LineMarker &other);
void SetXPM(const char *textForm);
void SetXPM(const char *const *linesForm);
void SetRGBAImage(Point sizeRGBAImage, float scale, const unsigned char *pixelsRGBAImage);
void Draw(Surface *surface, PRectangle &rcWhole, Font &fontForCharacter, typeOfFold tFold, int marginStyle) const;
};
}
#endif

View File

@@ -0,0 +1,470 @@
// Scintilla source code edit control
/** @file MarginView.cxx
** Defines the appearance of the editor margin.
**/
// Copyright 1998-2014 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstddef>
#include <cstdlib>
#include <cassert>
#include <cstring>
#include <cctype>
#include <cstdio>
#include <cmath>
#include <stdexcept>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <memory>
#include "Platform.h"
#include "ILoader.h"
#include "ILexer.h"
#include "Scintilla.h"
#include "Position.h"
#include "IntegerRectangle.h"
#include "UniqueString.h"
#include "SplitVector.h"
#include "Partitioning.h"
#include "RunStyles.h"
#include "ContractionState.h"
#include "CellBuffer.h"
#include "KeyMap.h"
#include "Indicator.h"
#include "LineMarker.h"
#include "Style.h"
#include "ViewStyle.h"
#include "CharClassify.h"
#include "Decoration.h"
#include "CaseFolder.h"
#include "Document.h"
#include "UniConversion.h"
#include "Selection.h"
#include "PositionCache.h"
#include "EditModel.h"
#include "MarginView.h"
#include "EditView.h"
using namespace Scintilla;
namespace Scintilla {
void DrawWrapMarker(Surface *surface, PRectangle rcPlace,
bool isEndMarker, ColourDesired wrapColour) {
surface->PenColour(wrapColour);
const IntegerRectangle ircPlace(rcPlace);
enum { xa = 1 }; // gap before start
const int w = ircPlace.Width() - xa - 1;
const bool xStraight = isEndMarker; // x-mirrored symbol for start marker
const int x0 = xStraight ? ircPlace.left : ircPlace.right - 1;
const int y0 = ircPlace.top;
const int dy = ircPlace.Height() / 5;
const int y = ircPlace.Height() / 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, 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);
}
MarginView::MarginView() {
wrapMarkerPaddingRight = 3;
customDrawWrapMarker = nullptr;
}
void MarginView::DropGraphics(bool freeObjects) {
if (freeObjects) {
pixmapSelMargin.reset();
pixmapSelPattern.reset();
pixmapSelPatternOffset1.reset();
} else {
if (pixmapSelMargin)
pixmapSelMargin->Release();
if (pixmapSelPattern)
pixmapSelPattern->Release();
if (pixmapSelPatternOffset1)
pixmapSelPatternOffset1->Release();
}
}
void MarginView::AllocateGraphics(const ViewStyle &vsDraw) {
if (!pixmapSelMargin)
pixmapSelMargin.reset(Surface::Allocate(vsDraw.technology));
if (!pixmapSelPattern)
pixmapSelPattern.reset(Surface::Allocate(vsDraw.technology));
if (!pixmapSelPatternOffset1)
pixmapSelPatternOffset1.reset(Surface::Allocate(vsDraw.technology));
}
void MarginView::RefreshPixMaps(Surface *surfaceWindow, WindowID wid, const ViewStyle &vsDraw) {
if (!pixmapSelPattern->Initialised()) {
const int patternSize = 8;
pixmapSelPattern->InitPixMap(patternSize, patternSize, surfaceWindow, wid);
pixmapSelPatternOffset1->InitPixMap(patternSize, patternSize, surfaceWindow, wid);
// 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.
const PRectangle rcPattern = PRectangle::FromInts(0, 0, patternSize, patternSize);
// Initialize default colours based on the chrome colour scheme. Typically the highlight is white.
ColourDesired colourFMFill = vsDraw.selbar;
ColourDesired colourFMStripes = vsDraw.selbarlight;
if (!(vsDraw.selbarlight == 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 = vsDraw.selbarlight;
}
if (vsDraw.foldmarginColour.isSet) {
// override default fold margin colour
colourFMFill = vsDraw.foldmarginColour;
}
if (vsDraw.foldmarginHighlightColour.isSet) {
// override default fold margin highlight colour
colourFMStripes = vsDraw.foldmarginHighlightColour;
}
pixmapSelPattern->FillRectangle(rcPattern, colourFMFill);
pixmapSelPatternOffset1->FillRectangle(rcPattern, colourFMStripes);
for (int y = 0; y < patternSize; y++) {
for (int x = y % 2; x < patternSize; x += 2) {
const PRectangle rcPixel = PRectangle::FromInts(x, y, x + 1, y + 1);
pixmapSelPattern->FillRectangle(rcPixel, colourFMStripes);
pixmapSelPatternOffset1->FillRectangle(rcPixel, colourFMFill);
}
}
}
}
static int SubstituteMarkerIfEmpty(int markerCheck, int markerDefault, const ViewStyle &vs) {
if (vs.markers[markerCheck].markType == SC_MARK_EMPTY)
return markerDefault;
return markerCheck;
}
void MarginView::PaintMargin(Surface *surface, Sci::Line topLine, PRectangle rc, PRectangle rcMargin,
const EditModel &model, const ViewStyle &vs) {
PRectangle rcSelMargin = rcMargin;
rcSelMargin.right = rcMargin.left;
if (rcSelMargin.bottom < rc.bottom)
rcSelMargin.bottom = rc.bottom;
const Point ptOrigin = model.GetVisibleOriginInMain();
FontAlias fontLineNumber = vs.styles[STYLE_LINENUMBER].font;
for (size_t margin = 0; margin < vs.ms.size(); 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) {
if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
// Required because of special way brush is created for selection margin
// Ensure patterns line up when scrolling with separate margin view
// by choosing correctly aligned variant.
const bool invertPhase = static_cast<int>(ptOrigin.y) & 1;
surface->FillRectangle(rcSelMargin,
invertPhase ? *pixmapSelPattern : *pixmapSelPatternOffset1);
} else {
ColourDesired colour;
switch (vs.ms[margin].style) {
case SC_MARGIN_BACK:
colour = vs.styles[STYLE_DEFAULT].back;
break;
case SC_MARGIN_FORE:
colour = vs.styles[STYLE_DEFAULT].fore;
break;
case SC_MARGIN_COLOUR:
colour = vs.ms[margin].back;
break;
default:
colour = vs.styles[STYLE_LINENUMBER].back;
break;
}
surface->FillRectangle(rcSelMargin, colour);
}
} else {
surface->FillRectangle(rcSelMargin, vs.styles[STYLE_LINENUMBER].back);
}
const int lineStartPaint = static_cast<int>(rcMargin.top + ptOrigin.y) / vs.lineHeight;
Sci::Line visibleLine = model.TopLineOfMain() + lineStartPaint;
Sci::Position yposScreen = lineStartPaint * vs.lineHeight - static_cast<Sci::Position>(ptOrigin.y);
// 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;
if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
const int level = model.pdoc->GetLevel(model.pcs->DocFromDisplay(visibleLine));
if (level & SC_FOLDLEVELWHITEFLAG) {
Sci::Line lineBack = model.pcs->DocFromDisplay(visibleLine);
int levelPrev = level;
while ((lineBack > 0) && (levelPrev & SC_FOLDLEVELWHITEFLAG)) {
lineBack--;
levelPrev = model.pdoc->GetLevel(lineBack);
}
if (!(levelPrev & SC_FOLDLEVELHEADERFLAG)) {
if (LevelNumber(level) < LevelNumber(levelPrev))
needWhiteClosure = true;
}
}
if (highlightDelimiter.isEnabled) {
const Sci::Line lastLine = model.pcs->DocFromDisplay(topLine + model.LinesOnScreen()) + 1;
model.pdoc->GetHighlightDelimiters(highlightDelimiter,
model.pdoc->SciLineFromPosition(model.sel.MainCaret()), lastLine);
}
}
// Old code does not know about new markers needed to distinguish all cases
const int folderOpenMid = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEROPENMID,
SC_MARKNUM_FOLDEROPEN, vs);
const int folderEnd = SubstituteMarkerIfEmpty(SC_MARKNUM_FOLDEREND,
SC_MARKNUM_FOLDER, vs);
while ((visibleLine < model.pcs->LinesDisplayed()) && yposScreen < rc.bottom) {
PLATFORM_ASSERT(visibleLine < model.pcs->LinesDisplayed());
const Sci::Line lineDoc = model.pcs->DocFromDisplay(visibleLine);
PLATFORM_ASSERT(model.pcs->GetVisible(lineDoc));
const Sci::Line firstVisibleLine = model.pcs->DisplayFromDoc(lineDoc);
const Sci::Line lastVisibleLine = model.pcs->DisplayLastFromDoc(lineDoc);
const bool firstSubLine = visibleLine == firstVisibleLine;
const bool lastSubLine = visibleLine == lastVisibleLine;
int marks = model.pdoc->GetMark(lineDoc);
if (!firstSubLine)
marks = 0;
bool headWithTail = false;
if (vs.ms[margin].mask & SC_MASK_FOLDERS) {
// Decide which fold indicator should be displayed
const int level = model.pdoc->GetLevel(lineDoc);
const int levelNext = model.pdoc->GetLevel(lineDoc + 1);
const int levelNum = LevelNumber(level);
const int levelNextNum = LevelNumber(levelNext);
if (level & SC_FOLDLEVELHEADERFLAG) {
if (firstSubLine) {
if (levelNum < levelNextNum) {
if (model.pcs->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 if (levelNum > SC_FOLDLEVELBASE) {
marks |= 1 << SC_MARKNUM_FOLDERSUB;
}
} else {
if (levelNum < levelNextNum) {
if (model.pcs->GetExpanded(lineDoc)) {
marks |= 1 << SC_MARKNUM_FOLDERSUB;
} else if (levelNum > SC_FOLDLEVELBASE) {
marks |= 1 << SC_MARKNUM_FOLDERSUB;
}
} else if (levelNum > SC_FOLDLEVELBASE) {
marks |= 1 << SC_MARKNUM_FOLDERSUB;
}
}
needWhiteClosure = false;
const Sci::Line firstFollowupLine = model.pcs->DocFromDisplay(model.pcs->DisplayFromDoc(lineDoc + 1));
const int firstFollowupLineLevel = model.pdoc->GetLevel(firstFollowupLine);
const int secondFollowupLineLevelNum = LevelNumber(model.pdoc->GetLevel(firstFollowupLine + 1));
if (!model.pcs->GetExpanded(lineDoc)) {
if ((firstFollowupLineLevel & SC_FOLDLEVELWHITEFLAG) &&
(levelNum > secondFollowupLineLevelNum))
needWhiteClosure = true;
if (highlightDelimiter.IsFoldBlockHighlighted(firstFollowupLine))
headWithTail = true;
}
} else if (level & SC_FOLDLEVELWHITEFLAG) {
if (needWhiteClosure) {
if (levelNext & SC_FOLDLEVELWHITEFLAG) {
marks |= 1 << SC_MARKNUM_FOLDERSUB;
} else if (levelNextNum > 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 (lastSubLine) {
if (levelNextNum > SC_FOLDLEVELBASE) {
marks |= 1 << SC_MARKNUM_FOLDERMIDTAIL;
} else {
marks |= 1 << SC_MARKNUM_FOLDERTAIL;
}
} else {
marks |= 1 << SC_MARKNUM_FOLDERSUB;
}
} else {
marks |= 1 << SC_MARKNUM_FOLDERSUB;
}
}
}
marks &= vs.ms[margin].mask;
PRectangle rcMarker = rcSelMargin;
rcMarker.top = static_cast<XYPOSITION>(yposScreen);
rcMarker.bottom = static_cast<XYPOSITION>(yposScreen + vs.lineHeight);
if (vs.ms[margin].style == SC_MARGIN_NUMBER) {
if (firstSubLine) {
std::string sNumber;
if (lineDoc >= 0) {
sNumber = std::to_string(lineDoc + 1);
}
if (model.foldFlags & (SC_FOLDFLAG_LEVELNUMBERS | SC_FOLDFLAG_LINESTATE)) {
char number[100] = "";
if (model.foldFlags & SC_FOLDFLAG_LEVELNUMBERS) {
const int lev = model.pdoc->GetLevel(lineDoc);
sprintf(number, "%c%c %03X %03X",
(lev & SC_FOLDLEVELHEADERFLAG) ? 'H' : '_',
(lev & SC_FOLDLEVELWHITEFLAG) ? 'W' : '_',
LevelNumber(lev),
lev >> 16
);
} else {
const int state = model.pdoc->GetLineState(lineDoc);
sprintf(number, "%0X", state);
}
sNumber = number;
}
PRectangle rcNumber = rcMarker;
// Right justify
const XYPOSITION width = surface->WidthText(fontLineNumber, sNumber.c_str(), static_cast<int>(sNumber.length()));
const XYPOSITION xpos = rcNumber.right - width - vs.marginNumberPadding;
rcNumber.left = xpos;
DrawTextNoClipPhase(surface, rcNumber, vs.styles[STYLE_LINENUMBER],
rcNumber.top + vs.maxAscent, sNumber.c_str(), static_cast<int>(sNumber.length()), drawAll);
} else if (vs.wrapVisualFlags & SC_WRAPVISUALFLAG_MARGIN) {
PRectangle rcWrapMarker = rcMarker;
rcWrapMarker.right -= wrapMarkerPaddingRight;
rcWrapMarker.left = rcWrapMarker.right - vs.styles[STYLE_LINENUMBER].aveCharWidth;
if (!customDrawWrapMarker) {
DrawWrapMarker(surface, rcWrapMarker, false, vs.styles[STYLE_LINENUMBER].fore);
} else {
customDrawWrapMarker(surface, rcWrapMarker, false, vs.styles[STYLE_LINENUMBER].fore);
}
}
} else if (vs.ms[margin].style == SC_MARGIN_TEXT || vs.ms[margin].style == SC_MARGIN_RTEXT) {
const StyledText stMargin = model.pdoc->MarginStyledText(lineDoc);
if (stMargin.text && ValidStyledText(vs, vs.marginStyleOffset, stMargin)) {
if (firstSubLine) {
surface->FillRectangle(rcMarker,
vs.styles[stMargin.StyleAt(0) + vs.marginStyleOffset].back);
if (vs.ms[margin].style == SC_MARGIN_RTEXT) {
const int width = WidestLineWidth(surface, vs, vs.marginStyleOffset, stMargin);
rcMarker.left = rcMarker.right - width - 3;
}
DrawStyledText(surface, vs, vs.marginStyleOffset, rcMarker,
stMargin, 0, stMargin.length, drawAll);
} else {
// if we're displaying annotation lines, color the margin to match the associated document line
const int annotationLines = model.pdoc->AnnotationLines(lineDoc);
if (annotationLines && (visibleLine > lastVisibleLine - annotationLines)) {
surface->FillRectangle(rcMarker, vs.styles[stMargin.StyleAt(0) + vs.marginStyleOffset].back);
}
}
}
}
if (marks) {
for (int markBit = 0; (markBit < 32) && marks; markBit++) {
if (marks & 1) {
LineMarker::typeOfFold tFold = LineMarker::undefined;
if ((vs.ms[margin].mask & SC_MASK_FOLDERS) && highlightDelimiter.IsFoldBlockHighlighted(lineDoc)) {
if (highlightDelimiter.IsBodyOfFoldBlock(lineDoc)) {
tFold = LineMarker::body;
} else if (highlightDelimiter.IsHeadOfFoldBlock(lineDoc)) {
if (firstSubLine) {
tFold = headWithTail ? LineMarker::headWithTail : LineMarker::head;
} else {
if (model.pcs->GetExpanded(lineDoc) || headWithTail) {
tFold = LineMarker::body;
} else {
tFold = LineMarker::undefined;
}
}
} else if (highlightDelimiter.IsTailOfFoldBlock(lineDoc)) {
tFold = LineMarker::tail;
}
}
vs.markers[markBit].Draw(surface, rcMarker, fontLineNumber, tFold, vs.ms[margin].style);
}
marks >>= 1;
}
}
visibleLine++;
yposScreen += vs.lineHeight;
}
}
}
PRectangle rcBlankMargin = rcMargin;
rcBlankMargin.left = rcSelMargin.right;
surface->FillRectangle(rcBlankMargin, vs.styles[STYLE_DEFAULT].back);
}
}

View File

@@ -0,0 +1,46 @@
// Scintilla source code edit control
/** @file MarginView.h
** Defines the appearance of the editor margin.
**/
// Copyright 1998-2014 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef MARGINVIEW_H
#define MARGINVIEW_H
namespace Scintilla {
void DrawWrapMarker(Surface *surface, PRectangle rcPlace, bool isEndMarker, ColourDesired wrapColour);
typedef void (*DrawWrapMarkerFn)(Surface *surface, PRectangle rcPlace, bool isEndMarker, ColourDesired wrapColour);
/**
* MarginView draws the margins.
*/
class MarginView {
public:
std::unique_ptr<Surface> pixmapSelMargin;
std::unique_ptr<Surface> pixmapSelPattern;
std::unique_ptr<Surface> pixmapSelPatternOffset1;
// Highlight current folding block
HighlightDelimiter highlightDelimiter;
int wrapMarkerPaddingRight; // right-most pixel padding of wrap markers
/** Some platforms, notably PLAT_CURSES, do not support Scintilla's native
* DrawWrapMarker function for drawing wrap markers. Allow those platforms to
* override it instead of creating a new method in the Surface class that
* existing platforms must implement as empty. */
DrawWrapMarkerFn customDrawWrapMarker;
MarginView();
void DropGraphics(bool freeObjects);
void AllocateGraphics(const ViewStyle &vsDraw);
void RefreshPixMaps(Surface *surfaceWindow, WindowID wid, const ViewStyle &vsDraw);
void PaintMargin(Surface *surface, Sci::Line topLine, PRectangle rc, PRectangle rcMargin,
const EditModel &model, const ViewStyle &vs);
};
}
#endif

View File

@@ -0,0 +1,204 @@
// Scintilla source code edit control
/** @file Partitioning.h
** Data structure used to partition an interval. Used for holding line start/end positions.
**/
// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef PARTITIONING_H
#define PARTITIONING_H
namespace Scintilla {
/// A split vector of integers with a method for adding a value to all elements
/// in a range.
/// Used by the Partitioning class.
template <typename T>
class SplitVectorWithRangeAdd : public SplitVector<T> {
public:
explicit SplitVectorWithRangeAdd(ptrdiff_t growSize_) {
this->SetGrowSize(growSize_);
this->ReAllocate(growSize_);
}
// Deleted so SplitVectorWithRangeAdd objects can not be copied.
SplitVectorWithRangeAdd(const SplitVectorWithRangeAdd &) = delete;
SplitVectorWithRangeAdd(SplitVectorWithRangeAdd &&) = delete;
void operator=(const SplitVectorWithRangeAdd &) = delete;
void operator=(SplitVectorWithRangeAdd &&) = delete;
~SplitVectorWithRangeAdd() {
}
void RangeAddDelta(ptrdiff_t start, ptrdiff_t end, T delta) noexcept {
// end is 1 past end, so end-start is number of elements to change
ptrdiff_t i = 0;
const ptrdiff_t rangeLength = end - start;
ptrdiff_t range1Length = rangeLength;
const ptrdiff_t part1Left = this->part1Length - start;
if (range1Length > part1Left)
range1Length = part1Left;
while (i < range1Length) {
this->body[start++] += delta;
i++;
}
start += this->gapLength;
while (i < rangeLength) {
this->body[start++] += delta;
i++;
}
}
};
/// Divide an interval into multiple partitions.
/// Useful for breaking a document down into sections such as lines.
/// A 0 length interval has a single 0 length partition, numbered 0
/// If interval not 0 length then each partition non-zero length
/// When needed, positions after the interval are considered part of the last partition
/// but the end of the last partition can be found with PositionFromPartition(last+1).
template <typename T>
class Partitioning {
private:
// To avoid calculating all the partition positions whenever any text is inserted
// there may be a step somewhere in the list.
T stepPartition;
T stepLength;
std::unique_ptr<SplitVectorWithRangeAdd<T>> body;
// Move step forward
void ApplyStep(T partitionUpTo) noexcept {
if (stepLength != 0) {
body->RangeAddDelta(stepPartition+1, partitionUpTo + 1, stepLength);
}
stepPartition = partitionUpTo;
if (stepPartition >= body->Length()-1) {
stepPartition = Partitions();
stepLength = 0;
}
}
// Move step backward
void BackStep(T partitionDownTo) noexcept {
if (stepLength != 0) {
body->RangeAddDelta(partitionDownTo+1, stepPartition+1, -stepLength);
}
stepPartition = partitionDownTo;
}
void Allocate(ptrdiff_t growSize) {
body.reset(new SplitVectorWithRangeAdd<T>(growSize));
stepPartition = 0;
stepLength = 0;
body->Insert(0, 0); // This value stays 0 for ever
body->Insert(1, 0); // This is the end of the first partition and will be the start of the second
}
public:
explicit Partitioning(int growSize) : stepPartition(0), stepLength(0) {
Allocate(growSize);
}
// Deleted so Partitioning objects can not be copied.
Partitioning(const Partitioning &) = delete;
Partitioning(Partitioning &&) = delete;
void operator=(const Partitioning &) = delete;
void operator=(Partitioning &&) = delete;
~Partitioning() {
}
T Partitions() const noexcept {
return static_cast<T>(body->Length())-1;
}
void InsertPartition(T partition, T pos) {
if (stepPartition < partition) {
ApplyStep(partition);
}
body->Insert(partition, pos);
stepPartition++;
}
void SetPartitionStartPosition(T partition, T pos) noexcept {
ApplyStep(partition+1);
if ((partition < 0) || (partition > body->Length())) {
return;
}
body->SetValueAt(partition, pos);
}
void InsertText(T partitionInsert, T delta) {
// Point all the partitions after the insertion point further along in the buffer
if (stepLength != 0) {
if (partitionInsert >= stepPartition) {
// Fill in up to the new insertion point
ApplyStep(partitionInsert);
stepLength += delta;
} else if (partitionInsert >= (stepPartition - body->Length() / 10)) {
// Close to step but before so move step back
BackStep(partitionInsert);
stepLength += delta;
} else {
ApplyStep(Partitions());
stepPartition = partitionInsert;
stepLength = delta;
}
} else {
stepPartition = partitionInsert;
stepLength = delta;
}
}
void RemovePartition(T partition) {
if (partition > stepPartition) {
ApplyStep(partition);
stepPartition--;
} else {
stepPartition--;
}
body->Delete(partition);
}
T PositionFromPartition(T partition) const noexcept {
PLATFORM_ASSERT(partition >= 0);
PLATFORM_ASSERT(partition < body->Length());
const ptrdiff_t lengthBody = body->Length();
if ((partition < 0) || (partition >= lengthBody)) {
return 0;
}
T pos = body->ValueAt(partition);
if (partition > stepPartition)
pos += stepLength;
return pos;
}
/// Return value in range [0 .. Partitions() - 1] even for arguments outside interval
T PartitionFromPosition(T pos) const noexcept {
if (body->Length() <= 1)
return 0;
if (pos >= (PositionFromPartition(Partitions())))
return Partitions() - 1;
T lower = 0;
T upper = Partitions();
do {
const T middle = (upper + lower + 1) / 2; // Round high
T posMiddle = body->ValueAt(middle);
if (middle > stepPartition)
posMiddle += stepLength;
if (pos < posMiddle) {
upper = middle - 1;
} else {
lower = middle;
}
} while (lower < upper);
return lower;
}
void DeleteAll() {
Allocate(body->GetGrowSize());
}
};
}
#endif

View File

@@ -0,0 +1,499 @@
// Scintilla source code edit control
/** @file PerLine.cxx
** Manages data associated with each line of the document
**/
// Copyright 1998-2009 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstddef>
#include <cassert>
#include <cstring>
#include <stdexcept>
#include <vector>
#include <forward_list>
#include <algorithm>
#include <memory>
#include "Platform.h"
#include "Scintilla.h"
#include "Position.h"
#include "SplitVector.h"
#include "Partitioning.h"
#include "CellBuffer.h"
#include "PerLine.h"
using namespace Scintilla;
MarkerHandleSet::MarkerHandleSet() {
}
MarkerHandleSet::~MarkerHandleSet() {
mhList.clear();
}
bool MarkerHandleSet::Empty() const noexcept {
return mhList.empty();
}
int MarkerHandleSet::MarkValue() const noexcept {
unsigned int m = 0;
for (const MarkerHandleNumber &mhn : mhList) {
m |= (1 << mhn.number);
}
return m;
}
bool MarkerHandleSet::Contains(int handle) const noexcept {
for (const MarkerHandleNumber &mhn : mhList) {
if (mhn.handle == handle) {
return true;
}
}
return false;
}
bool MarkerHandleSet::InsertHandle(int handle, int markerNum) {
mhList.push_front(MarkerHandleNumber(handle, markerNum));
return true;
}
void MarkerHandleSet::RemoveHandle(int handle) {
mhList.remove_if([handle](const MarkerHandleNumber &mhn) { return mhn.handle == handle; });
}
bool MarkerHandleSet::RemoveNumber(int markerNum, bool all) {
bool performedDeletion = false;
mhList.remove_if([&](const MarkerHandleNumber &mhn) {
if ((all || !performedDeletion) && (mhn.number == markerNum)) {
performedDeletion = true;
return true;
}
return false;
});
return performedDeletion;
}
void MarkerHandleSet::CombineWith(MarkerHandleSet *other) {
mhList.splice_after(mhList.before_begin(), other->mhList);
}
LineMarkers::~LineMarkers() {
markers.DeleteAll();
}
void LineMarkers::Init() {
markers.DeleteAll();
}
void LineMarkers::InsertLine(Sci::Line line) {
if (markers.Length()) {
markers.Insert(line, 0);
}
}
void LineMarkers::RemoveLine(Sci::Line line) {
// Retain the markers from the deleted line by oring them into the previous line
if (markers.Length()) {
if (line > 0) {
MergeMarkers(line - 1);
}
markers.Delete(line);
}
}
Sci::Line LineMarkers::LineFromHandle(int markerHandle) {
if (markers.Length()) {
for (Sci::Line line = 0; line < markers.Length(); line++) {
if (markers[line]) {
if (markers[line]->Contains(markerHandle)) {
return line;
}
}
}
}
return -1;
}
void LineMarkers::MergeMarkers(Sci::Line line) {
if (markers[line + 1]) {
if (!markers[line])
markers[line].reset(new MarkerHandleSet);
markers[line]->CombineWith(markers[line + 1].get());
markers[line + 1].reset();
}
}
int LineMarkers::MarkValue(Sci::Line line) noexcept {
if (markers.Length() && (line >= 0) && (line < markers.Length()) && markers[line])
return markers[line]->MarkValue();
else
return 0;
}
Sci::Line LineMarkers::MarkerNext(Sci::Line lineStart, int mask) const {
if (lineStart < 0)
lineStart = 0;
const Sci::Line length = static_cast<Sci::Line>(markers.Length());
for (Sci::Line iLine = lineStart; iLine < length; iLine++) {
const MarkerHandleSet *onLine = markers[iLine].get();
if (onLine && ((onLine->MarkValue() & mask) != 0))
return iLine;
}
return -1;
}
int LineMarkers::AddMark(Sci::Line line, int markerNum, Sci::Line lines) {
handleCurrent++;
if (!markers.Length()) {
// No existing markers so allocate one element per line
markers.InsertEmpty(0, lines);
}
if (line >= markers.Length()) {
return -1;
}
if (!markers[line]) {
// Need new structure to hold marker handle
markers[line].reset(new MarkerHandleSet());
}
markers[line]->InsertHandle(handleCurrent, markerNum);
return handleCurrent;
}
bool LineMarkers::DeleteMark(Sci::Line line, int markerNum, bool all) {
bool someChanges = false;
if (markers.Length() && (line >= 0) && (line < markers.Length()) && markers[line]) {
if (markerNum == -1) {
someChanges = true;
markers[line].reset();
} else {
someChanges = markers[line]->RemoveNumber(markerNum, all);
if (markers[line]->Empty()) {
markers[line].reset();
}
}
}
return someChanges;
}
void LineMarkers::DeleteMarkFromHandle(int markerHandle) {
const Sci::Line line = LineFromHandle(markerHandle);
if (line >= 0) {
markers[line]->RemoveHandle(markerHandle);
if (markers[line]->Empty()) {
markers[line].reset();
}
}
}
LineLevels::~LineLevels() {
}
void LineLevels::Init() {
levels.DeleteAll();
}
void LineLevels::InsertLine(Sci::Line line) {
if (levels.Length()) {
const int level = (line < levels.Length()) ? levels[line] : SC_FOLDLEVELBASE;
levels.InsertValue(line, 1, level);
}
}
void LineLevels::RemoveLine(Sci::Line line) {
if (levels.Length()) {
// Move up following lines but merge header flag from this line
// to line before to avoid a temporary disappearence causing expansion.
int firstHeader = levels[line] & SC_FOLDLEVELHEADERFLAG;
levels.Delete(line);
if (line == levels.Length()-1) // Last line loses the header flag
levels[line-1] &= ~SC_FOLDLEVELHEADERFLAG;
else if (line > 0)
levels[line-1] |= firstHeader;
}
}
void LineLevels::ExpandLevels(Sci::Line sizeNew) {
levels.InsertValue(levels.Length(), sizeNew - levels.Length(), SC_FOLDLEVELBASE);
}
void LineLevels::ClearLevels() {
levels.DeleteAll();
}
int LineLevels::SetLevel(Sci::Line line, int level, Sci::Line lines) {
int prev = 0;
if ((line >= 0) && (line < lines)) {
if (!levels.Length()) {
ExpandLevels(lines + 1);
}
prev = levels[line];
if (prev != level) {
levels[line] = level;
}
}
return prev;
}
int LineLevels::GetLevel(Sci::Line line) const {
if (levels.Length() && (line >= 0) && (line < levels.Length())) {
return levels[line];
} else {
return SC_FOLDLEVELBASE;
}
}
LineState::~LineState() {
}
void LineState::Init() {
lineStates.DeleteAll();
}
void LineState::InsertLine(Sci::Line line) {
if (lineStates.Length()) {
lineStates.EnsureLength(line);
const int val = (line < lineStates.Length()) ? lineStates[line] : 0;
lineStates.Insert(line, val);
}
}
void LineState::RemoveLine(Sci::Line line) {
if (lineStates.Length() > line) {
lineStates.Delete(line);
}
}
int LineState::SetLineState(Sci::Line line, int state) {
lineStates.EnsureLength(line + 1);
const int stateOld = lineStates[line];
lineStates[line] = state;
return stateOld;
}
int LineState::GetLineState(Sci::Line line) {
if (line < 0)
return 0;
lineStates.EnsureLength(line + 1);
return lineStates[line];
}
Sci::Line LineState::GetMaxLineState() const {
return static_cast<Sci::Line>(lineStates.Length());
}
static int NumberLines(const char *text) noexcept {
if (text) {
int newLines = 0;
while (*text) {
if (*text == '\n')
newLines++;
text++;
}
return newLines+1;
} else {
return 0;
}
}
// Each allocated LineAnnotation is a char array which starts with an AnnotationHeader
// and then has text and optional styles.
static const int IndividualStyles = 0x100;
struct AnnotationHeader {
short style; // Style IndividualStyles implies array of styles
short lines;
int length;
};
LineAnnotation::~LineAnnotation() {
ClearAll();
}
void LineAnnotation::Init() {
ClearAll();
}
void LineAnnotation::InsertLine(Sci::Line line) {
if (annotations.Length()) {
annotations.EnsureLength(line);
annotations.Insert(line, std::unique_ptr<char []>());
}
}
void LineAnnotation::RemoveLine(Sci::Line line) {
if (annotations.Length() && (line > 0) && (line <= annotations.Length())) {
annotations[line-1].reset();
annotations.Delete(line-1);
}
}
bool LineAnnotation::MultipleStyles(Sci::Line line) const {
if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])
return reinterpret_cast<AnnotationHeader *>(annotations[line].get())->style == IndividualStyles;
else
return false;
}
int LineAnnotation::Style(Sci::Line line) const {
if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])
return reinterpret_cast<AnnotationHeader *>(annotations[line].get())->style;
else
return 0;
}
const char *LineAnnotation::Text(Sci::Line line) const {
if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])
return annotations[line].get()+sizeof(AnnotationHeader);
else
return nullptr;
}
const unsigned char *LineAnnotation::Styles(Sci::Line line) const {
if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line] && MultipleStyles(line))
return reinterpret_cast<unsigned char *>(annotations[line].get() + sizeof(AnnotationHeader) + Length(line));
else
return nullptr;
}
static char *AllocateAnnotation(int length, int style) {
const size_t len = sizeof(AnnotationHeader) + length + ((style == IndividualStyles) ? length : 0);
char *ret = new char[len]();
return ret;
}
void LineAnnotation::SetText(Sci::Line line, const char *text) {
if (text && (line >= 0)) {
annotations.EnsureLength(line+1);
const int style = Style(line);
annotations[line].reset(AllocateAnnotation(static_cast<int>(strlen(text)), style));
char *pa = annotations[line].get();
assert(pa);
AnnotationHeader *pah = reinterpret_cast<AnnotationHeader *>(pa);
pah->style = static_cast<short>(style);
pah->length = static_cast<int>(strlen(text));
pah->lines = static_cast<short>(NumberLines(text));
memcpy(pa+sizeof(AnnotationHeader), text, pah->length);
} else {
if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line]) {
annotations[line].reset();
}
}
}
void LineAnnotation::ClearAll() {
annotations.DeleteAll();
}
void LineAnnotation::SetStyle(Sci::Line line, int style) {
annotations.EnsureLength(line+1);
if (!annotations[line]) {
annotations[line].reset(AllocateAnnotation(0, style));
}
reinterpret_cast<AnnotationHeader *>(annotations[line].get())->style = static_cast<short>(style);
}
void LineAnnotation::SetStyles(Sci::Line line, const unsigned char *styles) {
if (line >= 0) {
annotations.EnsureLength(line+1);
if (!annotations[line]) {
annotations[line].reset(AllocateAnnotation(0, IndividualStyles));
} else {
const AnnotationHeader *pahSource = reinterpret_cast<AnnotationHeader *>(annotations[line].get());
if (pahSource->style != IndividualStyles) {
char *allocation = AllocateAnnotation(pahSource->length, IndividualStyles);
AnnotationHeader *pahAlloc = reinterpret_cast<AnnotationHeader *>(allocation);
pahAlloc->length = pahSource->length;
pahAlloc->lines = pahSource->lines;
memcpy(allocation + sizeof(AnnotationHeader), annotations[line].get() + sizeof(AnnotationHeader), pahSource->length);
annotations[line].reset(allocation);
}
}
AnnotationHeader *pah = reinterpret_cast<AnnotationHeader *>(annotations[line].get());
pah->style = IndividualStyles;
memcpy(annotations[line].get() + sizeof(AnnotationHeader) + pah->length, styles, pah->length);
}
}
int LineAnnotation::Length(Sci::Line line) const {
if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])
return reinterpret_cast<AnnotationHeader *>(annotations[line].get())->length;
else
return 0;
}
int LineAnnotation::Lines(Sci::Line line) const {
if (annotations.Length() && (line >= 0) && (line < annotations.Length()) && annotations[line])
return reinterpret_cast<AnnotationHeader *>(annotations[line].get())->lines;
else
return 0;
}
LineTabstops::~LineTabstops() {
tabstops.DeleteAll();
}
void LineTabstops::Init() {
tabstops.DeleteAll();
}
void LineTabstops::InsertLine(Sci::Line line) {
if (tabstops.Length()) {
tabstops.EnsureLength(line);
tabstops.Insert(line, nullptr);
}
}
void LineTabstops::RemoveLine(Sci::Line line) {
if (tabstops.Length() > line) {
tabstops[line].reset();
tabstops.Delete(line);
}
}
bool LineTabstops::ClearTabstops(Sci::Line line) {
if (line < tabstops.Length()) {
TabstopList *tl = tabstops[line].get();
if (tl) {
tl->clear();
return true;
}
}
return false;
}
bool LineTabstops::AddTabstop(Sci::Line line, int x) {
tabstops.EnsureLength(line + 1);
if (!tabstops[line]) {
tabstops[line].reset(new TabstopList());
}
TabstopList *tl = tabstops[line].get();
if (tl) {
// tabstop positions are kept in order - insert in the right place
std::vector<int>::iterator it = std::lower_bound(tl->begin(), tl->end(), x);
// don't insert duplicates
if (it == tl->end() || *it != x) {
tl->insert(it, x);
return true;
}
}
return false;
}
int LineTabstops::GetNextTabstop(Sci::Line line, int x) const {
if (line < tabstops.Length()) {
TabstopList *tl = tabstops[line].get();
if (tl) {
for (const int i : *tl) {
if (i > x) {
return i;
}
}
}
}
return 0;
}

View File

@@ -0,0 +1,164 @@
// Scintilla source code edit control
/** @file PerLine.h
** Manages data associated with each line of the document
**/
// Copyright 1998-2009 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef PERLINE_H
#define PERLINE_H
namespace Scintilla {
/**
* This holds the marker identifier and the marker type to display.
* MarkerHandleNumbers are members of lists.
*/
struct MarkerHandleNumber {
int handle;
int number;
MarkerHandleNumber(int handle_, int number_) : handle(handle_), number(number_) {}
};
/**
* A marker handle set contains any number of MarkerHandleNumbers.
*/
class MarkerHandleSet {
std::forward_list<MarkerHandleNumber> mhList;
public:
MarkerHandleSet();
// Deleted so MarkerHandleSet objects can not be copied.
MarkerHandleSet(const MarkerHandleSet &) = delete;
MarkerHandleSet(MarkerHandleSet &&) = delete;
void operator=(const MarkerHandleSet &) = delete;
void operator=(MarkerHandleSet &&) = delete;
~MarkerHandleSet();
bool Empty() const noexcept;
int MarkValue() const noexcept; ///< Bit set of marker numbers.
bool Contains(int handle) const noexcept;
bool InsertHandle(int handle, int markerNum);
void RemoveHandle(int handle);
bool RemoveNumber(int markerNum, bool all);
void CombineWith(MarkerHandleSet *other);
};
class LineMarkers : public PerLine {
SplitVector<std::unique_ptr<MarkerHandleSet>> markers;
/// Handles are allocated sequentially and should never have to be reused as 32 bit ints are very big.
int handleCurrent;
public:
LineMarkers() : handleCurrent(0) {
}
// Deleted so LineMarkers objects can not be copied.
LineMarkers(const LineMarkers &) = delete;
LineMarkers(LineMarkers &&) = delete;
void operator=(const LineMarkers &) = delete;
void operator=(LineMarkers &&) = delete;
~LineMarkers() override;
void Init() override;
void InsertLine(Sci::Line line) override;
void RemoveLine(Sci::Line line) override;
int MarkValue(Sci::Line line) noexcept;
Sci::Line MarkerNext(Sci::Line lineStart, int mask) const;
int AddMark(Sci::Line line, int markerNum, Sci::Line lines);
void MergeMarkers(Sci::Line line);
bool DeleteMark(Sci::Line line, int markerNum, bool all);
void DeleteMarkFromHandle(int markerHandle);
Sci::Line LineFromHandle(int markerHandle);
};
class LineLevels : public PerLine {
SplitVector<int> levels;
public:
LineLevels() {
}
// Deleted so LineLevels objects can not be copied.
LineLevels(const LineLevels &) = delete;
LineLevels(LineLevels &&) = delete;
void operator=(const LineLevels &) = delete;
void operator=(LineLevels &&) = delete;
~LineLevels() override;
void Init() override;
void InsertLine(Sci::Line line) override;
void RemoveLine(Sci::Line line) override;
void ExpandLevels(Sci::Line sizeNew=-1);
void ClearLevels();
int SetLevel(Sci::Line line, int level, Sci::Line lines);
int GetLevel(Sci::Line line) const;
};
class LineState : public PerLine {
SplitVector<int> lineStates;
public:
LineState() {
}
// Deleted so LineState objects can not be copied.
LineState(const LineState &) = delete;
LineState(LineState &&) = delete;
void operator=(const LineState &) = delete;
void operator=(LineState &&) = delete;
~LineState() override;
void Init() override;
void InsertLine(Sci::Line line) override;
void RemoveLine(Sci::Line line) override;
int SetLineState(Sci::Line line, int state);
int GetLineState(Sci::Line line);
Sci::Line GetMaxLineState() const;
};
class LineAnnotation : public PerLine {
SplitVector<std::unique_ptr<char []>> annotations;
public:
LineAnnotation() {
}
// Deleted so LineAnnotation objects can not be copied.
LineAnnotation(const LineAnnotation &) = delete;
LineAnnotation(LineAnnotation &&) = delete;
void operator=(const LineAnnotation &) = delete;
void operator=(LineAnnotation &&) = delete;
~LineAnnotation() override;
void Init() override;
void InsertLine(Sci::Line line) override;
void RemoveLine(Sci::Line line) override;
bool MultipleStyles(Sci::Line line) const;
int Style(Sci::Line line) const;
const char *Text(Sci::Line line) const;
const unsigned char *Styles(Sci::Line line) const;
void SetText(Sci::Line line, const char *text);
void ClearAll();
void SetStyle(Sci::Line line, int style);
void SetStyles(Sci::Line line, const unsigned char *styles);
int Length(Sci::Line line) const;
int Lines(Sci::Line line) const;
};
typedef std::vector<int> TabstopList;
class LineTabstops : public PerLine {
SplitVector<std::unique_ptr<TabstopList>> tabstops;
public:
LineTabstops() {
}
// Deleted so LineTabstops objects can not be copied.
LineTabstops(const LineTabstops &) = delete;
LineTabstops(LineTabstops &&) = delete;
void operator=(const LineTabstops &) = delete;
void operator=(LineTabstops &&) = delete;
~LineTabstops() override;
void Init() override;
void InsertLine(Sci::Line line) override;
void RemoveLine(Sci::Line line) override;
bool ClearTabstops(Sci::Line line);
bool AddTabstop(Sci::Line line, int x);
int GetNextTabstop(Sci::Line line, int x) const;
};
}
#endif

View File

@@ -0,0 +1,31 @@
// Scintilla source code edit control
/** @file Position.h
** Defines global type name Position in the Sci internal namespace.
**/
// Copyright 2015 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef POSITION_H
#define POSITION_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.
* A Line is a document or screen line.
*/
namespace Sci {
typedef ptrdiff_t Position;
typedef ptrdiff_t Line;
const Position invalidPosition = -1;
template <typename T>
inline constexpr T clamp(T val, T minVal, T maxVal) {
return (val > maxVal) ? maxVal : ((val < minVal) ? minVal : val);
}
}
#endif

View File

@@ -0,0 +1,719 @@
// Scintilla source code edit control
/** @file PositionCache.cxx
** Classes for caching layout information.
**/
// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <stdexcept>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <iterator>
#include <memory>
#include "Platform.h"
#include "ILoader.h"
#include "ILexer.h"
#include "Scintilla.h"
#include "Position.h"
#include "UniqueString.h"
#include "SplitVector.h"
#include "Partitioning.h"
#include "RunStyles.h"
#include "ContractionState.h"
#include "CellBuffer.h"
#include "KeyMap.h"
#include "Indicator.h"
#include "LineMarker.h"
#include "Style.h"
#include "ViewStyle.h"
#include "CharClassify.h"
#include "Decoration.h"
#include "CaseFolder.h"
#include "Document.h"
#include "UniConversion.h"
#include "Selection.h"
#include "PositionCache.h"
using namespace Scintilla;
LineLayout::LineLayout(int maxLineLength_) :
lenLineStarts(0),
lineNumber(-1),
inCache(false),
maxLineLength(-1),
numCharsInLine(0),
numCharsBeforeEOL(0),
validity(llInvalid),
xHighlightGuide(0),
highlightColumn(false),
containsCaret(false),
edgeColumn(0),
bracePreviousStyles{},
hotspot(0,0),
widthLine(wrapWidthInfinite),
lines(1),
wrapIndent(0) {
Resize(maxLineLength_);
}
LineLayout::~LineLayout() {
Free();
}
void LineLayout::Resize(int maxLineLength_) {
if (maxLineLength_ > maxLineLength) {
Free();
chars.reset(new char[maxLineLength_ + 1]);
styles.reset(new unsigned char[maxLineLength_ + 1]);
// Extra position allocated as sometimes the Windows
// GetTextExtentExPoint API writes an extra element.
positions.reset(new XYPOSITION[maxLineLength_ + 1 + 1]);
maxLineLength = maxLineLength_;
}
}
void LineLayout::Free() {
chars.reset();
styles.reset();
positions.reset();
lineStarts.reset();
}
void LineLayout::Invalidate(validLevel validity_) {
if (validity > validity_)
validity = validity_;
}
int LineLayout::LineStart(int line) const {
if (line <= 0) {
return 0;
} else if ((line >= lines) || !lineStarts) {
return numCharsInLine;
} else {
return lineStarts[line];
}
}
int LineLayout::LineLastVisible(int line, Scope scope) const {
if (line < 0) {
return 0;
} else if ((line >= lines-1) || !lineStarts) {
return scope == Scope::visibleOnly ? numCharsBeforeEOL : numCharsInLine;
} else {
return lineStarts[line+1];
}
}
Range LineLayout::SubLineRange(int subLine, Scope scope) const {
return Range(LineStart(subLine), LineLastVisible(subLine, scope));
}
bool LineLayout::InLine(int offset, int line) const {
return ((offset >= LineStart(line)) && (offset < LineStart(line + 1))) ||
((offset == numCharsInLine) && (line == (lines-1)));
}
void LineLayout::SetLineStart(int line, int start) {
if ((line >= lenLineStarts) && (line != 0)) {
int newMaxLines = line + 20;
int *newLineStarts = new int[newMaxLines];
for (int i = 0; i < newMaxLines; i++) {
if (i < lenLineStarts)
newLineStarts[i] = lineStarts[i];
else
newLineStarts[i] = 0;
}
lineStarts.reset(newLineStarts);
lenLineStarts = newMaxLines;
}
lineStarts[line] = start;
}
void LineLayout::SetBracesHighlight(Range rangeLine, const Sci::Position braces[],
char bracesMatchStyle, int xHighlight, bool ignoreStyle) {
if (!ignoreStyle && rangeLine.ContainsCharacter(braces[0])) {
const Sci::Position braceOffset = braces[0] - rangeLine.start;
if (braceOffset < numCharsInLine) {
bracePreviousStyles[0] = styles[braceOffset];
styles[braceOffset] = bracesMatchStyle;
}
}
if (!ignoreStyle && rangeLine.ContainsCharacter(braces[1])) {
const Sci::Position 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, const Sci::Position braces[], bool ignoreStyle) {
if (!ignoreStyle && rangeLine.ContainsCharacter(braces[0])) {
const Sci::Position braceOffset = braces[0] - rangeLine.start;
if (braceOffset < numCharsInLine) {
styles[braceOffset] = bracePreviousStyles[0];
}
}
if (!ignoreStyle && rangeLine.ContainsCharacter(braces[1])) {
const Sci::Position braceOffset = braces[1] - rangeLine.start;
if (braceOffset < numCharsInLine) {
styles[braceOffset] = bracePreviousStyles[1];
}
}
xHighlightGuide = 0;
}
int LineLayout::FindBefore(XYPOSITION x, Range range) const {
Sci::Position lower = range.start;
Sci::Position upper = range.end;
do {
const Sci::Position middle = (upper + lower + 1) / 2; // Round high
const XYPOSITION posMiddle = positions[middle];
if (x < posMiddle) {
upper = middle - 1;
} else {
lower = middle;
}
} while (lower < upper);
return static_cast<int>(lower);
}
int LineLayout::FindPositionFromX(XYPOSITION x, Range range, bool charPosition) const {
int pos = FindBefore(x, range);
while (pos < range.end) {
if (charPosition) {
if (x < (positions[pos + 1])) {
return pos;
}
} else {
if (x < ((positions[pos] + positions[pos + 1]) / 2)) {
return pos;
}
}
pos++;
}
return static_cast<int>(range.end);
}
Point LineLayout::PointFromPosition(int posInLine, int lineHeight, PointEnd pe) const {
Point pt;
// In case of very long line put x at arbitrary large position
if (posInLine > maxLineLength) {
pt.x = positions[maxLineLength] - positions[LineStart(lines)];
}
for (int subLine = 0; subLine < lines; subLine++) {
const Range rangeSubLine = SubLineRange(subLine, Scope::visibleOnly);
if (posInLine >= rangeSubLine.start) {
pt.y = static_cast<XYPOSITION>(subLine*lineHeight);
if (posInLine <= rangeSubLine.end) {
pt.x = positions[posInLine] - positions[rangeSubLine.start];
if (rangeSubLine.start != 0) // Wrapped lines may be indented
pt.x += wrapIndent;
if (pe & peSubLineEnd) // Return end of first subline not start of next
break;
} else if ((pe & peLineEnd) && (subLine == (lines-1))) {
pt.x = positions[numCharsInLine] - positions[rangeSubLine.start];
if (rangeSubLine.start != 0) // Wrapped lines may be indented
pt.x += wrapIndent;
}
} else {
break;
}
}
return pt;
}
int LineLayout::EndLineStyle() const {
return styles[numCharsBeforeEOL > 0 ? numCharsBeforeEOL-1 : 0];
}
LineLayoutCache::LineLayoutCache() :
level(0),
allInvalidated(false), styleClock(-1), useCount(0) {
Allocate(0);
}
LineLayoutCache::~LineLayoutCache() {
Deallocate();
}
void LineLayoutCache::Allocate(size_t length_) {
PLATFORM_ASSERT(cache.empty());
allInvalidated = false;
cache.resize(length_);
}
void LineLayoutCache::AllocateForLevel(Sci::Line linesOnScreen, Sci::Line linesInDoc) {
PLATFORM_ASSERT(useCount == 0);
size_t lengthForLevel = 0;
if (level == llcCaret) {
lengthForLevel = 1;
} else if (level == llcPage) {
lengthForLevel = linesOnScreen + 1;
} else if (level == llcDocument) {
lengthForLevel = linesInDoc;
}
if (lengthForLevel > cache.size()) {
Deallocate();
Allocate(lengthForLevel);
} else {
if (lengthForLevel < cache.size()) {
for (size_t i = lengthForLevel; i < cache.size(); i++) {
cache[i].reset();
}
}
cache.resize(lengthForLevel);
}
PLATFORM_ASSERT(cache.size() == lengthForLevel);
}
void LineLayoutCache::Deallocate() {
PLATFORM_ASSERT(useCount == 0);
cache.clear();
}
void LineLayoutCache::Invalidate(LineLayout::validLevel validity_) {
if (!cache.empty() && !allInvalidated) {
for (const std::unique_ptr<LineLayout> &ll : cache) {
if (ll) {
ll->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(Sci::Line lineNumber, Sci::Line lineCaret, int maxChars, int styleClock_,
Sci::Line linesOnScreen, Sci::Line linesInDoc) {
AllocateForLevel(linesOnScreen, linesInDoc);
if (styleClock != styleClock_) {
Invalidate(LineLayout::llCheckTextAndStyle);
styleClock = styleClock_;
}
allInvalidated = false;
Sci::Position pos = -1;
LineLayout *ret = nullptr;
if (level == llcCaret) {
pos = 0;
} else if (level == llcPage) {
if (lineNumber == lineCaret) {
pos = 0;
} else if (cache.size() > 1) {
pos = 1 + (lineNumber % (cache.size() - 1));
}
} else if (level == llcDocument) {
pos = lineNumber;
}
if (pos >= 0) {
PLATFORM_ASSERT(useCount == 0);
if (!cache.empty() && (pos < static_cast<int>(cache.size()))) {
if (cache[pos]) {
if ((cache[pos]->lineNumber != lineNumber) ||
(cache[pos]->maxLineLength < maxChars)) {
cache[pos].reset();
}
}
if (!cache[pos]) {
cache[pos].reset(new LineLayout(maxChars));
}
cache[pos]->lineNumber = lineNumber;
cache[pos]->inCache = true;
ret = cache[pos].get();
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--;
}
}
}
// Simply pack the (maximum 4) character bytes into an int
static unsigned int KeyFromString(const char *charBytes, size_t len) {
PLATFORM_ASSERT(len <= 4);
unsigned int k=0;
for (size_t i=0; i<len && charBytes[i]; i++) {
k = k * 0x100;
const unsigned char uc = charBytes[i];
k += uc;
}
return k;
}
SpecialRepresentations::SpecialRepresentations() {
const short none = 0;
std::fill(startByteHasReprs, std::end(startByteHasReprs), none);
}
void SpecialRepresentations::SetRepresentation(const char *charBytes, const char *value) {
const unsigned int key = KeyFromString(charBytes, UTF8MaxBytes);
MapRepresentation::iterator it = mapReprs.find(key);
if (it == mapReprs.end()) {
// New entry so increment for first byte
const unsigned char ucStart = charBytes[0];
startByteHasReprs[ucStart]++;
}
mapReprs[key] = Representation(value);
}
void SpecialRepresentations::ClearRepresentation(const char *charBytes) {
MapRepresentation::iterator it = mapReprs.find(KeyFromString(charBytes, UTF8MaxBytes));
if (it != mapReprs.end()) {
mapReprs.erase(it);
const unsigned char ucStart = charBytes[0];
startByteHasReprs[ucStart]--;
}
}
const Representation *SpecialRepresentations::RepresentationFromCharacter(const char *charBytes, size_t len) const {
PLATFORM_ASSERT(len <= 4);
const unsigned char ucStart = charBytes[0];
if (!startByteHasReprs[ucStart])
return nullptr;
MapRepresentation::const_iterator it = mapReprs.find(KeyFromString(charBytes, len));
if (it != mapReprs.end()) {
return &(it->second);
}
return nullptr;
}
bool SpecialRepresentations::Contains(const char *charBytes, size_t len) const {
PLATFORM_ASSERT(len <= 4);
const unsigned char ucStart = charBytes[0];
if (!startByteHasReprs[ucStart])
return false;
MapRepresentation::const_iterator it = mapReprs.find(KeyFromString(charBytes, len));
return it != mapReprs.end();
}
void SpecialRepresentations::Clear() {
mapReprs.clear();
const short none = 0;
std::fill(startByteHasReprs, std::end(startByteHasReprs), none);
}
void BreakFinder::Insert(Sci::Position val) {
const int posInLine = static_cast<int>(val);
if (posInLine > nextBreak) {
const std::vector<int>::iterator it = std::lower_bound(selAndEdge.begin(), selAndEdge.end(), posInLine);
if (it == selAndEdge.end()) {
selAndEdge.push_back(posInLine);
} else if (*it != posInLine) {
selAndEdge.insert(it, 1, posInLine);
}
}
}
BreakFinder::BreakFinder(const LineLayout *ll_, const Selection *psel, Range lineRange_, Sci::Position posLineStart_,
int xStart, bool breakForSelection, const Document *pdoc_, const SpecialRepresentations *preprs_, const ViewStyle *pvsDraw) :
ll(ll_),
lineRange(lineRange_),
posLineStart(posLineStart_),
nextBreak(static_cast<int>(lineRange_.start)),
saeCurrentPos(0),
saeNext(0),
subBreak(-1),
pdoc(pdoc_),
encodingFamily(pdoc_->CodePageFamily()),
preprs(preprs_) {
// Search for first visible break
// First find the first visible character
if (xStart > 0.0f)
nextBreak = ll->FindBefore(static_cast<XYPOSITION>(xStart), lineRange);
// Now back to a style break
while ((nextBreak > lineRange.start) && (ll->styles[nextBreak] == ll->styles[nextBreak - 1])) {
nextBreak--;
}
if (breakForSelection) {
const SelectionPosition posStart(posLineStart);
const SelectionPosition posEnd(posLineStart + lineRange.end);
const SelectionSegment segmentLine(posStart, posEnd);
for (size_t r=0; r<psel->Count(); r++) {
const SelectionSegment portion = psel->Range(r).Intersect(segmentLine);
if (!(portion.start == portion.end)) {
if (portion.start.IsValid())
Insert(portion.start.Position() - posLineStart);
if (portion.end.IsValid())
Insert(portion.end.Position() - posLineStart);
}
}
}
if (pvsDraw && pvsDraw->indicatorsSetFore) {
for (const IDecoration *deco : pdoc->decorations->View()) {
if (pvsDraw->indicators[deco->Indicator()].OverridesTextFore()) {
Sci::Position startPos = deco->EndRun(posLineStart);
while (startPos < (posLineStart + lineRange.end)) {
Insert(startPos - posLineStart);
startPos = deco->EndRun(startPos);
}
}
}
}
Insert(ll->edgeColumn);
Insert(lineRange.end);
saeNext = (!selAndEdge.empty()) ? selAndEdge[0] : -1;
}
BreakFinder::~BreakFinder() {
}
TextSegment BreakFinder::Next() {
if (subBreak == -1) {
const int prev = nextBreak;
while (nextBreak < lineRange.end) {
int charWidth = 1;
if (encodingFamily == efUnicode)
charWidth = UTF8DrawBytes(reinterpret_cast<unsigned char *>(&ll->chars[nextBreak]),
static_cast<int>(lineRange.end - nextBreak));
else if (encodingFamily == efDBCS)
charWidth = pdoc->DBCSDrawBytes(
&ll->chars[nextBreak], static_cast<int>(lineRange.end - nextBreak));
const Representation *repr = preprs->RepresentationFromCharacter(&ll->chars[nextBreak], charWidth);
if (((nextBreak > 0) && (ll->styles[nextBreak] != ll->styles[nextBreak - 1])) ||
repr ||
(nextBreak == saeNext)) {
while ((nextBreak >= saeNext) && (saeNext < lineRange.end)) {
saeCurrentPos++;
saeNext = static_cast<int>((saeCurrentPos < selAndEdge.size()) ? selAndEdge[saeCurrentPos] : lineRange.end);
}
if ((nextBreak > prev) || repr) {
// Have a segment to report
if (nextBreak == prev) {
nextBreak += charWidth;
} else {
repr = nullptr; // Optimize -> should remember repr
}
if ((nextBreak - prev) < lengthStartSubdivision) {
return TextSegment(prev, nextBreak - prev, repr);
} else {
break;
}
}
}
nextBreak += charWidth;
}
if ((nextBreak - prev) < lengthStartSubdivision) {
return TextSegment(prev, nextBreak - prev);
}
subBreak = prev;
}
// Splitting up a long run from prev to nextBreak in lots of approximately lengthEachSubdivision.
// For very long runs add extra breaks after spaces or if no spaces before low punctuation.
const int startSegment = subBreak;
if ((nextBreak - subBreak) <= lengthEachSubdivision) {
subBreak = -1;
return TextSegment(startSegment, nextBreak - startSegment);
} else {
subBreak += pdoc->SafeSegment(&ll->chars[subBreak], nextBreak-subBreak, lengthEachSubdivision);
if (subBreak >= nextBreak) {
subBreak = -1;
return TextSegment(startSegment, nextBreak - startSegment);
} else {
return TextSegment(startSegment, subBreak - startSegment);
}
}
}
bool BreakFinder::More() const {
return (subBreak >= 0) || (nextBreak < lineRange.end);
}
PositionCacheEntry::PositionCacheEntry() :
styleNumber(0), len(0), clock(0), positions(nullptr) {
}
// Copy constructor not currently used, but needed for being element in std::vector.
PositionCacheEntry::PositionCacheEntry(const PositionCacheEntry &other) :
styleNumber(other.styleNumber), len(other.styleNumber), clock(other.styleNumber), positions(nullptr) {
if (other.positions) {
const size_t lenData = len + (len / sizeof(XYPOSITION)) + 1;
positions.reset(new XYPOSITION[lenData]);
memcpy(positions.get(), other.positions.get(), lenData * sizeof(XYPOSITION));
}
}
void PositionCacheEntry::Set(unsigned int styleNumber_, const char *s_,
unsigned int len_, XYPOSITION *positions_, unsigned int clock_) {
Clear();
styleNumber = styleNumber_;
len = len_;
clock = clock_;
if (s_ && positions_) {
positions.reset(new XYPOSITION[len + (len / sizeof(XYPOSITION)) + 1]);
for (unsigned int i=0; i<len; i++) {
positions[i] = positions_[i];
}
memcpy(&positions[len], s_, len);
}
}
PositionCacheEntry::~PositionCacheEntry() {
Clear();
}
void PositionCacheEntry::Clear() {
positions.reset();
styleNumber = 0;
len = 0;
clock = 0;
}
bool PositionCacheEntry::Retrieve(unsigned int styleNumber_, const char *s_,
unsigned int len_, XYPOSITION *positions_) const {
if ((styleNumber == styleNumber_) && (len == len_) &&
(memcmp(&positions[len], s_, len)== 0)) {
for (unsigned int i=0; i<len; i++) {
positions_[i] = positions[i];
}
return true;
} else {
return false;
}
}
unsigned int PositionCacheEntry::Hash(unsigned int styleNumber_, const char *s, unsigned int len_) {
unsigned int ret = s[0] << 7;
for (unsigned int i=0; i<len_; i++) {
ret *= 1000003;
ret ^= s[i];
}
ret *= 1000003;
ret ^= len_;
ret *= 1000003;
ret ^= styleNumber_;
return ret;
}
bool PositionCacheEntry::NewerThan(const PositionCacheEntry &other) const {
return clock > other.clock;
}
void PositionCacheEntry::ResetClock() {
if (clock > 0) {
clock = 1;
}
}
PositionCache::PositionCache() {
clock = 1;
pces.resize(0x400);
allClear = true;
}
PositionCache::~PositionCache() {
Clear();
}
void PositionCache::Clear() {
if (!allClear) {
for (PositionCacheEntry &pce : pces) {
pce.Clear();
}
}
clock = 1;
allClear = true;
}
void PositionCache::SetSize(size_t size_) {
Clear();
pces.resize(size_);
}
void PositionCache::MeasureWidths(Surface *surface, const ViewStyle &vstyle, unsigned int styleNumber,
const char *s, unsigned int len, XYPOSITION *positions, const Document *pdoc) {
allClear = false;
size_t probe = pces.size(); // Out of bounds
if ((!pces.empty()) && (len < 30)) {
// Only store short strings in the cache so it doesn't churn with
// long comments with only a single comment.
// Two way associative: try two probe positions.
const unsigned int hashValue = PositionCacheEntry::Hash(styleNumber, s, len);
probe = hashValue % pces.size();
if (pces[probe].Retrieve(styleNumber, s, len, positions)) {
return;
}
const unsigned int probe2 = (hashValue * 37) % pces.size();
if (pces[probe2].Retrieve(styleNumber, s, len, positions)) {
return;
}
// Not found. Choose the oldest of the two slots to replace
if (pces[probe].NewerThan(pces[probe2])) {
probe = probe2;
}
}
if (len > BreakFinder::lengthStartSubdivision) {
// Break up into segments
unsigned int startSegment = 0;
XYPOSITION xStartSegment = 0;
while (startSegment < len) {
const unsigned int lenSegment = pdoc->SafeSegment(s + startSegment, len - startSegment, BreakFinder::lengthEachSubdivision);
FontAlias fontStyle = vstyle.styles[styleNumber].font;
surface->MeasureWidths(fontStyle, s + startSegment, lenSegment, positions + startSegment);
for (unsigned int inSeg = 0; inSeg < lenSegment; inSeg++) {
positions[startSegment + inSeg] += xStartSegment;
}
xStartSegment = positions[startSegment + lenSegment - 1];
startSegment += lenSegment;
}
} else {
FontAlias fontStyle = vstyle.styles[styleNumber].font;
surface->MeasureWidths(fontStyle, s, len, positions);
}
if (probe < pces.size()) {
// Store into cache
clock++;
if (clock > 60000) {
// Since there are only 16 bits for the clock, wrap it round and
// reset all cache entries so none get stuck with a high clock.
for (PositionCacheEntry &pce : pces) {
pce.ResetClock();
}
clock = 2;
}
pces[probe].Set(styleNumber, s, len, positions, clock);
}
}

View File

@@ -0,0 +1,247 @@
// Scintilla source code edit control
/** @file PositionCache.h
** Classes for caching layout information.
**/
// Copyright 1998-2009 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef POSITIONCACHE_H
#define POSITIONCACHE_H
namespace Scintilla {
inline constexpr bool IsEOLChar(int ch) noexcept {
return (ch == '\r') || (ch == '\n');
}
inline constexpr bool IsSpaceOrTab(int ch) noexcept {
return ch == ' ' || ch == '\t';
}
/**
* A point in document space.
* Uses double for sufficient resolution in large (>20,000,000 line) documents.
*/
class PointDocument {
public:
double x;
double y;
explicit PointDocument(double x_ = 0, double y_ = 0) noexcept : x(x_), y(y_) {
}
// Conversion from Point.
explicit PointDocument(Point pt) noexcept : x(pt.x), y(pt.y) {
}
};
// There are two points for some positions and this enumeration
// can choose between the end of the first line or subline
// and the start of the next line or subline.
enum PointEnd {
peDefault = 0x0,
peLineEnd = 0x1,
peSubLineEnd = 0x2
};
/**
*/
class LineLayout {
private:
friend class LineLayoutCache;
std::unique_ptr<int []>lineStarts;
int lenLineStarts;
/// Drawing is only performed for @a maxLineLength characters on each line.
Sci::Line lineNumber;
bool inCache;
public:
enum { wrapWidthInfinite = 0x7ffffff };
int maxLineLength;
int numCharsInLine;
int numCharsBeforeEOL;
enum validLevel { llInvalid, llCheckTextAndStyle, llPositions, llLines } validity;
int xHighlightGuide;
bool highlightColumn;
bool containsCaret;
int edgeColumn;
std::unique_ptr<char[]> chars;
std::unique_ptr<unsigned char[]> styles;
std::unique_ptr<XYPOSITION[]> positions;
char bracePreviousStyles[2];
// Hotspot support
Range hotspot;
// Wrapped line support
int widthLine;
int lines;
XYPOSITION wrapIndent; // In pixels
explicit LineLayout(int maxLineLength_);
// Deleted so LineLayout objects can not be copied.
LineLayout(const LineLayout &) = delete;
LineLayout(LineLayout &&) = delete;
void operator=(const LineLayout &) = delete;
void operator=(LineLayout &&) = delete;
virtual ~LineLayout();
void Resize(int maxLineLength_);
void Free();
void Invalidate(validLevel validity_);
int LineStart(int line) const;
enum class Scope { visibleOnly, includeEnd };
int LineLastVisible(int line, Scope scope) const;
Range SubLineRange(int subLine, Scope scope) const;
bool InLine(int offset, int line) const;
void SetLineStart(int line, int start);
void SetBracesHighlight(Range rangeLine, const Sci::Position braces[],
char bracesMatchStyle, int xHighlight, bool ignoreStyle);
void RestoreBracesHighlight(Range rangeLine, const Sci::Position braces[], bool ignoreStyle);
int FindBefore(XYPOSITION x, Range range) const;
int FindPositionFromX(XYPOSITION x, Range range, bool charPosition) const;
Point PointFromPosition(int posInLine, int lineHeight, PointEnd pe) const;
int EndLineStyle() const;
};
/**
*/
class LineLayoutCache {
int level;
std::vector<std::unique_ptr<LineLayout>>cache;
bool allInvalidated;
int styleClock;
int useCount;
void Allocate(size_t length_);
void AllocateForLevel(Sci::Line linesOnScreen, Sci::Line linesInDoc);
public:
LineLayoutCache();
// Deleted so LineLayoutCache objects can not be copied.
LineLayoutCache(const LineLayoutCache &) = delete;
LineLayoutCache(LineLayoutCache &&) = delete;
void operator=(const LineLayoutCache &) = delete;
void operator=(LineLayoutCache &&) = delete;
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() const { return level; }
LineLayout *Retrieve(Sci::Line lineNumber, Sci::Line lineCaret, int maxChars, int styleClock_,
Sci::Line linesOnScreen, Sci::Line linesInDoc);
void Dispose(LineLayout *ll);
};
class PositionCacheEntry {
unsigned int styleNumber:8;
unsigned int len:8;
unsigned int clock:16;
std::unique_ptr<XYPOSITION []> positions;
public:
PositionCacheEntry();
// Copy constructor not currently used, but needed for being element in std::vector.
PositionCacheEntry(const PositionCacheEntry &);
// PositionCacheEntry objects should not be moved but MSVC 2015 requires this.
PositionCacheEntry(PositionCacheEntry &&) = default;
void operator=(const PositionCacheEntry &) = delete;
void operator=(PositionCacheEntry &&) = delete;
~PositionCacheEntry();
void Set(unsigned int styleNumber_, const char *s_, unsigned int len_, XYPOSITION *positions_, unsigned int clock_);
void Clear();
bool Retrieve(unsigned int styleNumber_, const char *s_, unsigned int len_, XYPOSITION *positions_) const;
static unsigned int Hash(unsigned int styleNumber_, const char *s, unsigned int len_);
bool NewerThan(const PositionCacheEntry &other) const;
void ResetClock();
};
class Representation {
public:
std::string stringRep;
explicit Representation(const char *value="") : stringRep(value) {
}
};
typedef std::map<unsigned int, Representation> MapRepresentation;
class SpecialRepresentations {
MapRepresentation mapReprs;
short startByteHasReprs[0x100];
public:
SpecialRepresentations();
void SetRepresentation(const char *charBytes, const char *value);
void ClearRepresentation(const char *charBytes);
const Representation *RepresentationFromCharacter(const char *charBytes, size_t len) const;
bool Contains(const char *charBytes, size_t len) const;
void Clear();
};
struct TextSegment {
int start;
int length;
const Representation *representation;
TextSegment(int start_=0, int length_=0, const Representation *representation_=nullptr) noexcept :
start(start_), length(length_), representation(representation_) {
}
int end() const noexcept {
return start + length;
}
};
// Class to break a line of text into shorter runs at sensible places.
class BreakFinder {
const LineLayout *ll;
Range lineRange;
Sci::Position posLineStart;
int nextBreak;
std::vector<int> selAndEdge;
unsigned int saeCurrentPos;
int saeNext;
int subBreak;
const Document *pdoc;
EncodingFamily encodingFamily;
const SpecialRepresentations *preprs;
void Insert(Sci::Position val);
public:
// If a whole run is longer than lengthStartSubdivision then subdivide
// into smaller runs at spaces or punctuation.
enum { lengthStartSubdivision = 300 };
// Try to make each subdivided run lengthEachSubdivision or shorter.
enum { lengthEachSubdivision = 100 };
BreakFinder(const LineLayout *ll_, const Selection *psel, Range lineRange_, Sci::Position posLineStart_,
int xStart, bool breakForSelection, const Document *pdoc_, const SpecialRepresentations *preprs_, const ViewStyle *pvsDraw);
// Deleted so BreakFinder objects can not be copied.
BreakFinder(const BreakFinder &) = delete;
BreakFinder(BreakFinder &&) = delete;
void operator=(const BreakFinder &) = delete;
void operator=(BreakFinder &&) = delete;
~BreakFinder();
TextSegment Next();
bool More() const;
};
class PositionCache {
std::vector<PositionCacheEntry> pces;
unsigned int clock;
bool allClear;
public:
PositionCache();
// Deleted so PositionCache objects can not be copied.
PositionCache(const PositionCache &) = delete;
PositionCache(PositionCache &&) = delete;
void operator=(const PositionCache &) = delete;
void operator=(PositionCache &&) = delete;
~PositionCache();
void Clear();
void SetSize(size_t size_);
size_t GetSize() const { return pces.size(); }
void MeasureWidths(Surface *surface, const ViewStyle &vstyle, unsigned int styleNumber,
const char *s, unsigned int len, XYPOSITION *positions, const Document *pdoc);
};
}
#endif

View File

@@ -0,0 +1,960 @@
// 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.
* Some extensions by Philippe Lhoste PhiLho(a)GMX.net
* '?' extensions by Michael Mullin masmullin@gmail.com
*
* 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 *pattern, 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)
*
* 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, \r, \t, \v match the corresponding C
* escape char, respectively BEL, BS, FF, LF, CR, TAB and VT;
* Note that \r and \n are never matched because Scintilla
* regex searches are made line per line
* (stripped of end-of-line chars).
* - if not in posix mode, when followed by a
* left or right round bracket (see [8]);
* - when followed by a digit 1 to 9 (see [9]);
* - when followed by a left or right angle bracket
* (see [10]);
* - when followed by d, D, s, S, w or W (see [11]);
* - when followed by x and two hexa digits (see [12].
* Backslash is used as an escape character for all
* other meta-characters, and itself.
*
* [4] [set] matches one of the characters in the set.
* If the first character in the set is "^",
* it matches the characters NOT in the set, i.e.
* complements the set. A shorthand S-E (start dash end)
* is used to specify a set of characters S up to
* E, inclusive. S and E must be characters, otherwise
* the dash is taken literally (eg. in expression [\d-a]).
* 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]
* (or just backslash them).
* examples: match:
*
* [-]|] matches these 3 chars,
*
* []-|] matches from ] to | chars
*
* [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]
* (except [8], [9] and [10] forms of [3]),
* followed by closure char (*)
* matches zero or more matches of that form.
*
* [6] + same as [5], except it matches one or more.
*
* [5-6] Both [5] and [6] are greedy (they match as much as possible).
* Unless they are followed by the 'lazy' quantifier (?)
* In which case both [5] and [6] try to match as little as possible
*
* [7] ? same as [5] except it matches zero or one.
*
* [8] a regular expression in the form [1] to [13], enclosed
* as \(form\) (or (form) with posix flag) matches what
* form matches. The enclosure creates a set of tags,
* used for [9] and for pattern substitution.
* The tagged forms are numbered starting from 1.
*
* [9] a \ followed by a digit 1 to 9 matches whatever a
* previously tagged regular expression ([8]) matched.
*
* [10] \< 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 _. Scintilla extends this definition
* by user setting. The word must also be preceded and/or
* followed by any character outside those mentioned.
*
* [11] \l a backslash followed by d, D, s, S, w or W,
* becomes a character class (both inside and
* outside sets []).
* d: decimal digits
* D: any char except decimal digits
* s: whitespace (space, \t \n \r \f \v)
* S: any char except whitespace (see above)
* w: alphanumeric & underscore (changed by user setting)
* W: any char except alphanumeric & underscore (see above)
*
* [12] \xHH a backslash followed by x and two hexa digits,
* becomes the character whose Ascii code is equal
* to these digits. If not followed by two digits,
* it is 'x' char itself.
*
* [13] a composite regular expression xy where x and y
* are in the form [1] to [12] matches the longest
* match of x followed by a match for y.
*
* [14] ^ 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 <cstddef>
#include <cstdlib>
#include <stdexcept>
#include <string>
#include <algorithm>
#include <iterator>
#include "Position.h"
#include "CharClassify.h"
#include "RESearch.h"
using namespace Scintilla;
#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 CLQ 12 /* 0 to 1 closure */
#define LCLO 13 /* lazy closure */
#define END 0
/*
* The following defines are not meant to be changeable.
* They are for readability only.
*/
#define BLKIND 0370
#define BITIND 07
static 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) {
failure = 0;
charClass = charClassTable;
sta = NOP; /* status of lastpat */
bol = 0;
const unsigned char nul=0;
std::fill(bittab, std::end(bittab), nul);
std::fill(tagstk, std::end(tagstk), 0);
std::fill(nfa, std::end(nfa), '\0');
Clear();
}
RESearch::~RESearch() {
Clear();
}
void RESearch::Clear() {
for (int i = 0; i < MAXTAG; i++) {
pat[i].clear();
bopat[i] = NOTFOUND;
eopat[i] = NOTFOUND;
}
}
void RESearch::GrabMatches(const CharacterIndexer &ci) {
for (unsigned int i = 0; i < MAXTAG; i++) {
if ((bopat[i] != NOTFOUND) && (eopat[i] != NOTFOUND)) {
Sci::Position len = eopat[i] - bopat[i];
pat[i].resize(len);
for (Sci::Position j = 0; j < len; j++)
pat[i][j] = ci.CharAt(bopat[i] + j);
}
}
}
void RESearch::ChSet(unsigned char c) {
bittab[((c) & BLKIND) >> 3] |= bitarr[(c) & BITIND];
}
void RESearch::ChSetWithCase(unsigned char c, bool caseSensitive) {
ChSet(c);
if (!caseSensitive) {
if ((c >= 'a') && (c <= 'z')) {
ChSet(c - 'a' + 'A');
} else if ((c >= 'A') && (c <= 'Z')) {
ChSet(c - 'A' + 'a');
}
}
}
static unsigned char escapeValue(unsigned 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;
}
static int GetHexaChar(unsigned char hd1, unsigned char hd2) {
int hexValue = 0;
if (hd1 >= '0' && hd1 <= '9') {
hexValue += 16 * (hd1 - '0');
} else if (hd1 >= 'A' && hd1 <= 'F') {
hexValue += 16 * (hd1 - 'A' + 10);
} else if (hd1 >= 'a' && hd1 <= 'f') {
hexValue += 16 * (hd1 - 'a' + 10);
} else {
return -1;
}
if (hd2 >= '0' && hd2 <= '9') {
hexValue += hd2 - '0';
} else if (hd2 >= 'A' && hd2 <= 'F') {
hexValue += hd2 - 'A' + 10;
} else if (hd2 >= 'a' && hd2 <= 'f') {
hexValue += hd2 - 'a' + 10;
} else {
return -1;
}
return hexValue;
}
/**
* Called when the parser finds a backslash not followed
* by a valid expression (like \( in non-Posix mode).
* @param pattern : pointer on the char after the backslash.
* @param incr : (out) number of chars to skip after expression evaluation.
* @return the char if it resolves to a simple char,
* or -1 for a char class. In this case, bittab is changed.
*/
int RESearch::GetBackslashExpression(
const char *pattern,
int &incr) {
// Since error reporting is primitive and messages are not used anyway,
// I choose to interpret unexpected syntax in a logical way instead
// of reporting errors. Otherwise, we can stick on, eg., PCRE behavior.
incr = 0; // Most of the time, will skip the char "naturally".
int c;
int result = -1;
const unsigned char bsc = *pattern;
if (!bsc) {
// Avoid overrun
result = '\\'; // \ at end of pattern, take it literally
return result;
}
switch (bsc) {
case 'a':
case 'b':
case 'n':
case 'f':
case 'r':
case 't':
case 'v':
result = escapeValue(bsc);
break;
case 'x': {
const unsigned char hd1 = *(pattern + 1);
const unsigned char hd2 = *(pattern + 2);
const int hexValue = GetHexaChar(hd1, hd2);
if (hexValue >= 0) {
result = hexValue;
incr = 2; // Must skip the digits
} else {
result = 'x'; // \x without 2 digits: see it as 'x'
}
}
break;
case 'd':
for (c = '0'; c <= '9'; c++) {
ChSet(static_cast<unsigned char>(c));
}
break;
case 'D':
for (c = 0; c < MAXCHR; c++) {
if (c < '0' || c > '9') {
ChSet(static_cast<unsigned char>(c));
}
}
break;
case 's':
ChSet(' ');
ChSet('\t');
ChSet('\n');
ChSet('\r');
ChSet('\f');
ChSet('\v');
break;
case 'S':
for (c = 0; c < MAXCHR; c++) {
if (c != ' ' && !(c >= 0x09 && c <= 0x0D)) {
ChSet(static_cast<unsigned char>(c));
}
}
break;
case 'w':
for (c = 0; c < MAXCHR; c++) {
if (iswordc(static_cast<unsigned char>(c))) {
ChSet(static_cast<unsigned char>(c));
}
}
break;
case 'W':
for (c = 0; c < MAXCHR; c++) {
if (!iswordc(static_cast<unsigned char>(c))) {
ChSet(static_cast<unsigned char>(c));
}
}
break;
default:
result = bsc;
}
return result;
}
const char *RESearch::Compile(const char *pattern, Sci::Position 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, prevChar;
if (!pattern || !length) {
if (sta)
return nullptr;
else
return badpat("No previous regular expression");
}
sta = NOP;
const char *p=pattern; /* 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 == pattern) {
*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;
prevChar = 0;
i++;
if (*++p == '^') {
mask = '\377';
i++;
p++;
} else {
mask = 0;
}
if (*p == '-') { /* real dash */
i++;
prevChar = *p;
ChSet(*p++);
}
if (*p == ']') { /* real brace */
i++;
prevChar = *p;
ChSet(*p++);
}
while (*p && *p != ']') {
if (*p == '-') {
if (prevChar < 0) {
// Previous def. was a char class like \d, take dash literally
prevChar = *p;
ChSet(*p);
} else if (*(p+1)) {
if (*(p+1) != ']') {
c1 = prevChar + 1;
i++;
c2 = static_cast<unsigned char>(*++p);
if (c2 == '\\') {
if (!*(p+1)) { // End of RE
return badpat("Missing ]");
} else {
i++;
p++;
int incr;
c2 = GetBackslashExpression(p, incr);
i += incr;
p += incr;
if (c2 >= 0) {
// Convention: \c (c is any char) is case sensitive, whatever the option
ChSet(static_cast<unsigned char>(c2));
prevChar = c2;
} else {
// bittab is already changed
prevChar = -1;
}
}
}
if (prevChar < 0) {
// Char after dash is char class like \d, take dash literally
prevChar = '-';
ChSet('-');
} else {
// Put all chars between c1 and c2 included in the char set
while (c1 <= c2) {
ChSetWithCase(static_cast<unsigned char>(c1++), caseSensitive);
}
}
} else {
// Dash before the ], take it literally
prevChar = *p;
ChSet(*p);
}
} else {
return badpat("Missing ]");
}
} else if (*p == '\\' && *(p+1)) {
i++;
p++;
int incr;
int c = GetBackslashExpression(p, incr);
i += incr;
p += incr;
if (c >= 0) {
// Convention: \c (c is any char) is case sensitive, whatever the option
ChSet(static_cast<unsigned char>(c));
prevChar = c;
} else {
// bittab is already changed
prevChar = -1;
}
} else {
prevChar = static_cast<unsigned char>(*p);
ChSetWithCase(*p, caseSensitive);
}
i++;
p++;
}
if (!*p)
return badpat("Missing ]");
for (n = 0; n < BITBLK; bittab[n++] = 0)
*mp++ = static_cast<char>(mask ^ bittab[n]);
break;
case '*': /* match 0 or more... */
case '+': /* match 1 or more... */
case '?':
if (p == pattern)
return badpat("Empty closure");
lp = sp; /* previous opcode */
if (*lp == CLO || *lp == LCLO) /* 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];
if (*p == '?') *mp = CLQ;
else if (*(p+1) == '?') *mp = LCLO;
else *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++ = REF;
*mp++ = static_cast<char>(n);
} else {
return badpat("Undetermined reference");
}
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++ = EOT;
*mp++ = static_cast<char>(tagstk[tagi--]);
} else {
return badpat("Unmatched \\)");
}
} else {
int incr;
int c = GetBackslashExpression(p, incr);
i += incr;
p += incr;
if (c >= 0) {
*mp++ = CHR;
*mp++ = static_cast<unsigned char>(c);
} else {
*mp++ = CCL;
mask = 0;
for (n = 0; n < BITBLK; bittab[n++] = 0)
*mp++ = static_cast<char>(mask ^ bittab[n]);
}
}
}
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++ = EOT;
*mp++ = static_cast<char>(tagstk[tagi--]);
} else {
return badpat("Unmatched )");
}
} else {
unsigned char c = *p;
if (!c) // End of RE
c = '\\'; // We take it as raw backslash
if (caseSensitive || !iswordc(c)) {
*mp++ = CHR;
*mp++ = c;
} else {
*mp++ = CCL;
mask = 0;
ChSetWithCase(c, false);
for (n = 0; n < BITBLK; bittab[n++] = 0)
*mp++ = static_cast<char>(mask ^ bittab[n]);
}
}
break;
}
sp = lp;
}
if (tagi > 0)
return badpat((posix ? "Unmatched (" : "Unmatched \\("));
*mp = END;
sta = OKP;
return nullptr;
}
/*
* 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(const CharacterIndexer &ci, Sci::Position lp, Sci::Position endp) {
unsigned char c;
Sci::Position 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) && (static_cast<unsigned char>(ci.CharAt(lp)) != c))
lp++;
if (lp >= endp) /* if EOS, fail, else fall through. */
return 0;
// Falls through.
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 up to 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);
static inline int isinset(const char *ap, unsigned char c) {
return ap[(c & BLKIND) >> 3] & bitarr[c & 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 */
Sci::Position RESearch::PMatch(const CharacterIndexer &ci, Sci::Position lp, Sci::Position endp, char *ap) {
int op, c, n;
Sci::Position e; /* extra pointer for CLO */
Sci::Position bp; /* beginning of subpat... */
Sci::Position ep; /* ending of subpat... */
Sci::Position are; /* to save the line ptr. */
Sci::Position llp; /* lazy lp for LCLO */
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:
if (lp >= endp)
return NOTFOUND;
if (!isinset(ap, ci.CharAt(lp++)))
return NOTFOUND;
ap += BITBLK;
break;
case BOL:
if (lp != bol)
return NOTFOUND;
break;
case EOL:
if (lp < endp)
return NOTFOUND;
break;
case BOT:
bopat[static_cast<int>(*ap++)] = lp;
break;
case EOT:
eopat[static_cast<int>(*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 LCLO:
case CLQ:
case CLO:
are = lp;
switch (*ap) {
case ANY:
if (op == CLO || op == LCLO)
while (lp < endp)
lp++;
else if (lp < endp)
lp++;
n = ANYSKIP;
break;
case CHR:
c = *(ap+1);
if (op == CLO || op == LCLO)
while ((lp < endp) && (c == ci.CharAt(lp)))
lp++;
else if ((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;
llp = lp;
e = NOTFOUND;
while (llp >= are) {
Sci::Position q;
if ((q = PMatch(ci, llp, endp, ap)) != NOTFOUND) {
e = q;
lp = llp;
if (op != LCLO) return e;
}
if (*ap == END) return e;
--llp;
}
if (*ap == EOT)
PMatch(ci, lp, endp, ap);
return e;
default:
//re_fail("RESearch::Execute: bad nfa.", static_cast<char>(op));
return NOTFOUND;
}
return lp;
}

View File

@@ -0,0 +1,70 @@
// 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
namespace Scintilla {
/*
* 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(Sci::Position index) const=0;
virtual ~CharacterIndexer() {
}
};
class RESearch {
public:
explicit RESearch(CharClassify *charClassTable);
// No dynamic allocation so default copy constructor and assignment operator are OK.
~RESearch();
void Clear();
void GrabMatches(const CharacterIndexer &ci);
const char *Compile(const char *pattern, Sci::Position length, bool caseSensitive, bool posix);
int Execute(const CharacterIndexer &ci, Sci::Position lp, Sci::Position endp);
enum { MAXTAG=10 };
enum { MAXNFA=4096 };
enum { NOTFOUND=-1 };
Sci::Position bopat[MAXTAG];
Sci::Position eopat[MAXTAG];
std::string pat[MAXTAG];
private:
void ChSet(unsigned char c);
void ChSetWithCase(unsigned char c, bool caseSensitive);
int GetBackslashExpression(const char *pattern, int &incr);
Sci::Position PMatch(const CharacterIndexer &ci, Sci::Position lp, Sci::Position endp, char *ap);
Sci::Position bol;
Sci::Position tagstk[MAXTAG]; /* subpat tag stack */
char nfa[MAXNFA]; /* automaton */
int sta;
unsigned char bittab[BITBLK]; /* bit table for CCL pre-set bits */
int failure;
CharClassify *charClass;
bool iswordc(unsigned char x) const {
return charClass->IsWord(x);
}
};
}
#endif

View File

@@ -0,0 +1,314 @@
/** @file RunStyles.cxx
** Data structure used to store sparse styles.
**/
// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstddef>
#include <cstdlib>
#include <cstdint>
#include <cstring>
#include <cstdio>
#include <cstdarg>
#include <climits>
#include <stdexcept>
#include <vector>
#include <algorithm>
#include <memory>
#include "Platform.h"
#include "Scintilla.h"
#include "Position.h"
#include "SplitVector.h"
#include "Partitioning.h"
#include "RunStyles.h"
using namespace Scintilla;
// Find the first run at a position
template <typename DISTANCE, typename STYLE>
DISTANCE RunStyles<DISTANCE, STYLE>::RunFromPosition(DISTANCE position) const noexcept {
DISTANCE run = starts->PartitionFromPosition(position);
// Go to first element with this position
while ((run > 0) && (position == starts->PositionFromPartition(run-1))) {
run--;
}
return run;
}
// If there is no run boundary at position, insert one continuing style.
template <typename DISTANCE, typename STYLE>
DISTANCE RunStyles<DISTANCE, STYLE>::SplitRun(DISTANCE position) {
DISTANCE run = RunFromPosition(position);
const DISTANCE posRun = starts->PositionFromPartition(run);
if (posRun < position) {
STYLE runStyle = ValueAt(position);
run++;
starts->InsertPartition(run, position);
styles->InsertValue(run, 1, runStyle);
}
return run;
}
template <typename DISTANCE, typename STYLE>
void RunStyles<DISTANCE, STYLE>::RemoveRun(DISTANCE run) {
starts->RemovePartition(run);
styles->DeleteRange(run, 1);
}
template <typename DISTANCE, typename STYLE>
void RunStyles<DISTANCE, STYLE>::RemoveRunIfEmpty(DISTANCE run) {
if ((run < starts->Partitions()) && (starts->Partitions() > 1)) {
if (starts->PositionFromPartition(run) == starts->PositionFromPartition(run+1)) {
RemoveRun(run);
}
}
}
template <typename DISTANCE, typename STYLE>
void RunStyles<DISTANCE, STYLE>::RemoveRunIfSameAsPrevious(DISTANCE run) {
if ((run > 0) && (run < starts->Partitions())) {
if (styles->ValueAt(run-1) == styles->ValueAt(run)) {
RemoveRun(run);
}
}
}
template <typename DISTANCE, typename STYLE>
RunStyles<DISTANCE, STYLE>::RunStyles() {
starts.reset(new Partitioning<DISTANCE>(8));
styles.reset(new SplitVector<STYLE>());
styles->InsertValue(0, 2, 0);
}
template <typename DISTANCE, typename STYLE>
RunStyles<DISTANCE, STYLE>::~RunStyles() {
}
template <typename DISTANCE, typename STYLE>
DISTANCE RunStyles<DISTANCE, STYLE>::Length() const noexcept {
return starts->PositionFromPartition(starts->Partitions());
}
template <typename DISTANCE, typename STYLE>
STYLE RunStyles<DISTANCE, STYLE>::ValueAt(DISTANCE position) const noexcept {
return styles->ValueAt(starts->PartitionFromPosition(position));
}
template <typename DISTANCE, typename STYLE>
DISTANCE RunStyles<DISTANCE, STYLE>::FindNextChange(DISTANCE position, DISTANCE end) const noexcept {
const DISTANCE run = starts->PartitionFromPosition(position);
if (run < starts->Partitions()) {
const DISTANCE runChange = starts->PositionFromPartition(run);
if (runChange > position)
return runChange;
const DISTANCE nextChange = starts->PositionFromPartition(run + 1);
if (nextChange > position) {
return nextChange;
} else if (position < end) {
return end;
} else {
return end + 1;
}
} else {
return end + 1;
}
}
template <typename DISTANCE, typename STYLE>
DISTANCE RunStyles<DISTANCE, STYLE>::StartRun(DISTANCE position) const noexcept {
return starts->PositionFromPartition(starts->PartitionFromPosition(position));
}
template <typename DISTANCE, typename STYLE>
DISTANCE RunStyles<DISTANCE, STYLE>::EndRun(DISTANCE position) const noexcept {
return starts->PositionFromPartition(starts->PartitionFromPosition(position) + 1);
}
template <typename DISTANCE, typename STYLE>
FillResult<DISTANCE> RunStyles<DISTANCE, STYLE>::FillRange(DISTANCE position, STYLE value, DISTANCE fillLength) {
const FillResult<DISTANCE> resultNoChange{false, position, fillLength};
if (fillLength <= 0) {
return resultNoChange;
}
DISTANCE end = position + fillLength;
if (end > Length()) {
return resultNoChange;
}
DISTANCE runEnd = RunFromPosition(end);
if (styles->ValueAt(runEnd) == value) {
// End already has value so trim range.
end = starts->PositionFromPartition(runEnd);
if (position >= end) {
// Whole range is already same as value so no action
return resultNoChange;
}
fillLength = end - position;
} else {
runEnd = SplitRun(end);
}
DISTANCE runStart = RunFromPosition(position);
if (styles->ValueAt(runStart) == value) {
// Start is in expected value so trim range.
runStart++;
position = starts->PositionFromPartition(runStart);
fillLength = end - position;
} else {
if (starts->PositionFromPartition(runStart) < position) {
runStart = SplitRun(position);
runEnd++;
}
}
if (runStart < runEnd) {
const FillResult<DISTANCE> result{ true, position, fillLength };
styles->SetValueAt(runStart, value);
// Remove each old run over the range
for (DISTANCE run=runStart+1; run<runEnd; run++) {
RemoveRun(runStart+1);
}
runEnd = RunFromPosition(end);
RemoveRunIfSameAsPrevious(runEnd);
RemoveRunIfSameAsPrevious(runStart);
runEnd = RunFromPosition(end);
RemoveRunIfEmpty(runEnd);
return result;
} else {
return resultNoChange;
}
}
template <typename DISTANCE, typename STYLE>
void RunStyles<DISTANCE, STYLE>::SetValueAt(DISTANCE position, STYLE value) {
FillRange(position, value, 1);
}
template <typename DISTANCE, typename STYLE>
void RunStyles<DISTANCE, STYLE>::InsertSpace(DISTANCE position, DISTANCE insertLength) {
DISTANCE runStart = RunFromPosition(position);
if (starts->PositionFromPartition(runStart) == position) {
STYLE runStyle = ValueAt(position);
// Inserting at start of run so make previous longer
if (runStart == 0) {
// Inserting at start of document so ensure 0
if (runStyle) {
styles->SetValueAt(0, STYLE());
starts->InsertPartition(1, 0);
styles->InsertValue(1, 1, runStyle);
starts->InsertText(0, insertLength);
} else {
starts->InsertText(runStart, insertLength);
}
} else {
if (runStyle) {
starts->InsertText(runStart-1, insertLength);
} else {
// Insert at end of run so do not extend style
starts->InsertText(runStart, insertLength);
}
}
} else {
starts->InsertText(runStart, insertLength);
}
}
template <typename DISTANCE, typename STYLE>
void RunStyles<DISTANCE, STYLE>::DeleteAll() {
starts.reset(new Partitioning<DISTANCE>(8));
styles.reset(new SplitVector<STYLE>());
styles->InsertValue(0, 2, 0);
}
template <typename DISTANCE, typename STYLE>
void RunStyles<DISTANCE, STYLE>::DeleteRange(DISTANCE position, DISTANCE deleteLength) {
DISTANCE end = position + deleteLength;
DISTANCE runStart = RunFromPosition(position);
DISTANCE runEnd = RunFromPosition(end);
if (runStart == runEnd) {
// Deleting from inside one run
starts->InsertText(runStart, -deleteLength);
RemoveRunIfEmpty(runStart);
} else {
runStart = SplitRun(position);
runEnd = SplitRun(end);
starts->InsertText(runStart, -deleteLength);
// Remove each old run over the range
for (DISTANCE run=runStart; run<runEnd; run++) {
RemoveRun(runStart);
}
RemoveRunIfEmpty(runStart);
RemoveRunIfSameAsPrevious(runStart);
}
}
template <typename DISTANCE, typename STYLE>
DISTANCE RunStyles<DISTANCE, STYLE>::Runs() const noexcept {
return starts->Partitions();
}
template <typename DISTANCE, typename STYLE>
bool RunStyles<DISTANCE, STYLE>::AllSame() const noexcept {
for (int run = 1; run < starts->Partitions(); run++) {
if (styles->ValueAt(run) != styles->ValueAt(run - 1))
return false;
}
return true;
}
template <typename DISTANCE, typename STYLE>
bool RunStyles<DISTANCE, STYLE>::AllSameAs(STYLE value) const noexcept {
return AllSame() && (styles->ValueAt(0) == value);
}
template <typename DISTANCE, typename STYLE>
DISTANCE RunStyles<DISTANCE, STYLE>::Find(STYLE value, DISTANCE start) const noexcept {
if (start < Length()) {
DISTANCE run = start ? RunFromPosition(start) : 0;
if (styles->ValueAt(run) == value)
return start;
run++;
while (run < starts->Partitions()) {
if (styles->ValueAt(run) == value)
return starts->PositionFromPartition(run);
run++;
}
}
return -1;
}
template <typename DISTANCE, typename STYLE>
void RunStyles<DISTANCE, STYLE>::Check() const {
if (Length() < 0) {
throw std::runtime_error("RunStyles: Length can not be negative.");
}
if (starts->Partitions() < 1) {
throw std::runtime_error("RunStyles: Must always have 1 or more partitions.");
}
if (starts->Partitions() != styles->Length()-1) {
throw std::runtime_error("RunStyles: Partitions and styles different lengths.");
}
DISTANCE start=0;
while (start < Length()) {
const DISTANCE end = EndRun(start);
if (start >= end) {
throw std::runtime_error("RunStyles: Partition is 0 length.");
}
start = end;
}
if (styles->ValueAt(styles->Length()-1) != 0) {
throw std::runtime_error("RunStyles: Unused style at end changed.");
}
for (int j=1; j<styles->Length()-1; j++) {
if (styles->ValueAt(j) == styles->ValueAt(j-1)) {
throw std::runtime_error("RunStyles: Style of a partition same as previous.");
}
}
}
template class Scintilla::RunStyles<int, int>;
template class Scintilla::RunStyles<int, char>;
#if (PTRDIFF_MAX != INT_MAX) || PLAT_HAIKU
template class Scintilla::RunStyles<ptrdiff_t, int>;
template class Scintilla::RunStyles<ptrdiff_t, char>;
#endif

View File

@@ -0,0 +1,64 @@
/** @file RunStyles.h
** Data structure used to store sparse styles.
**/
// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
/// Styling buffer using one element for each run rather than using
/// a filled buffer.
#ifndef RUNSTYLES_H
#define RUNSTYLES_H
namespace Scintilla {
// Return for RunStyles::FillRange reports if anything was changed and the
// range that was changed. This may be trimmed from the requested range
// when some of the requested range already had the requested value.
template <typename DISTANCE>
struct FillResult {
bool changed;
DISTANCE position;
DISTANCE fillLength;
};
template <typename DISTANCE, typename STYLE>
class RunStyles {
private:
std::unique_ptr<Partitioning<DISTANCE>> starts;
std::unique_ptr<SplitVector<STYLE>> styles;
DISTANCE RunFromPosition(DISTANCE position) const noexcept;
DISTANCE SplitRun(DISTANCE position);
void RemoveRun(DISTANCE run);
void RemoveRunIfEmpty(DISTANCE run);
void RemoveRunIfSameAsPrevious(DISTANCE run);
public:
RunStyles();
// Deleted so RunStyles objects can not be copied.
RunStyles(const RunStyles &) = delete;
RunStyles(RunStyles &&) = delete;
void operator=(const RunStyles &) = delete;
void operator=(RunStyles &&) = delete;
~RunStyles();
DISTANCE Length() const noexcept;
STYLE ValueAt(DISTANCE position) const noexcept;
DISTANCE FindNextChange(DISTANCE position, DISTANCE end) const noexcept;
DISTANCE StartRun(DISTANCE position) const noexcept;
DISTANCE EndRun(DISTANCE position) const noexcept;
// Returns changed=true if some values may have changed
FillResult<DISTANCE> FillRange(DISTANCE position, STYLE value, DISTANCE fillLength);
void SetValueAt(DISTANCE position, STYLE value);
void InsertSpace(DISTANCE position, DISTANCE insertLength);
void DeleteAll();
void DeleteRange(DISTANCE position, DISTANCE deleteLength);
DISTANCE Runs() const noexcept;
bool AllSame() const noexcept;
bool AllSameAs(STYLE value) const noexcept;
DISTANCE Find(STYLE value, DISTANCE start) const noexcept;
void Check() const;
};
}
#endif

View File

@@ -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
command.build.*.h=nmake -f scintilla.mak QUIET=1

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,103 @@
// 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
namespace Scintilla {
#ifdef SCI_LEXER
class LexState;
#endif
/**
*/
class ScintillaBase : public Editor, IListBoxDelegate {
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
};
enum { maxLenInputIME = 200 };
int displayPopupMenu;
Menu popup;
AutoComplete ac;
CallTip ct;
int listType; ///< 0 is an autocomplete list
int maxListWidth; /// Maximum width of list, in average character widths
int multiAutoCMode; /// Mode for autocompleting when multiple selections are present
#ifdef SCI_LEXER
LexState *DocumentLexState();
void SetLexer(uptr_t wParam);
void SetLexerLanguage(const char *languageName);
void Colourise(int start, int end);
#endif
ScintillaBase();
// Deleted so ScintillaBase objects can not be copied.
ScintillaBase(const ScintillaBase &) = delete;
ScintillaBase(ScintillaBase &&) = delete;
ScintillaBase &operator=(const ScintillaBase &) = delete;
ScintillaBase &operator=(ScintillaBase &&) = delete;
~ScintillaBase() override;
void Initialise() override {}
void Finalise() override;
void AddCharUTF(const char *s, unsigned int len, bool treatAsDBCS=false) override;
void Command(int cmdId);
void CancelModes() override;
int KeyCommand(unsigned int iMessage) override;
void AutoCompleteInsert(Sci::Position startPos, Sci::Position removeLen, const char *text, Sci::Position textLen);
void AutoCompleteStart(Sci::Position lenEntered, const char *list);
void AutoCompleteCancel();
void AutoCompleteMove(int delta);
int AutoCompleteGetCurrent() const;
int AutoCompleteGetCurrentText(char *buffer) const;
void AutoCompleteCharacterAdded(char ch);
void AutoCompleteCharacterDeleted();
void AutoCompleteCompleted(char ch, unsigned int completionMethod);
void AutoCompleteMoveToCurrentWord();
void AutoCompleteSelection();
void ListNotify(ListBoxEvent *plbe) override;
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;
bool ShouldDisplayPopup(Point ptInWindowCoordinates) const;
void ContextMenu(Point pt);
void ButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers) override;
void RightButtonDownWithModifiers(Point pt, unsigned int curTime, int modifiers) override;
void NotifyStyleToNeeded(Sci::Position endStyleNeeded) override;
void NotifyLexerChanged(Document *doc, void *userData) override;
public:
// Public so scintilla_send_message can use it
sptr_t WndProc(unsigned int iMessage, uptr_t wParam, sptr_t lParam) override;
};
}
#endif

View File

@@ -0,0 +1,436 @@
// Scintilla source code edit control
/** @file Selection.cxx
** Classes maintaining the selection.
**/
// Copyright 2009 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#include <cstddef>
#include <cstdlib>
#include <stdexcept>
#include <vector>
#include <algorithm>
#include <memory>
#include "Platform.h"
#include "Scintilla.h"
#include "Position.h"
#include "Selection.h"
using namespace Scintilla;
void SelectionPosition::MoveForInsertDelete(bool insertion, Sci::Position startChange, Sci::Position length) {
if (insertion) {
if (position == startChange) {
const Sci::Position virtualLengthRemove = std::min(length, virtualSpace);
virtualSpace -= virtualLengthRemove;
position += virtualLengthRemove;
} else if (position > startChange) {
position += length;
}
} else {
if (position == startChange) {
virtualSpace = 0;
}
if (position > startChange) {
const Sci::Position endDeletion = startChange + length;
if (position > endDeletion) {
position -= length;
} else {
position = startChange;
virtualSpace = 0;
}
}
}
}
bool SelectionPosition::operator <(const SelectionPosition &other) const {
if (position == other.position)
return virtualSpace < other.virtualSpace;
else
return position < other.position;
}
bool SelectionPosition::operator >(const SelectionPosition &other) const {
if (position == other.position)
return virtualSpace > other.virtualSpace;
else
return position > other.position;
}
bool SelectionPosition::operator <=(const SelectionPosition &other) const {
if (position == other.position && virtualSpace == other.virtualSpace)
return true;
else
return other > *this;
}
bool SelectionPosition::operator >=(const SelectionPosition &other) const {
if (position == other.position && virtualSpace == other.virtualSpace)
return true;
else
return *this > other;
}
Sci::Position SelectionRange::Length() const {
if (anchor > caret) {
return anchor.Position() - caret.Position();
} else {
return caret.Position() - anchor.Position();
}
}
void SelectionRange::MoveForInsertDelete(bool insertion, Sci::Position startChange, Sci::Position length) {
caret.MoveForInsertDelete(insertion, startChange, length);
anchor.MoveForInsertDelete(insertion, startChange, length);
}
bool SelectionRange::Contains(Sci::Position pos) const {
if (anchor > caret)
return (pos >= caret.Position()) && (pos <= anchor.Position());
else
return (pos >= anchor.Position()) && (pos <= caret.Position());
}
bool SelectionRange::Contains(SelectionPosition sp) const {
if (anchor > caret)
return (sp >= caret) && (sp <= anchor);
else
return (sp >= anchor) && (sp <= caret);
}
bool SelectionRange::ContainsCharacter(Sci::Position posCharacter) const {
if (anchor > caret)
return (posCharacter >= caret.Position()) && (posCharacter < anchor.Position());
else
return (posCharacter >= anchor.Position()) && (posCharacter < caret.Position());
}
SelectionSegment SelectionRange::Intersect(SelectionSegment check) const {
const SelectionSegment inOrder(caret, anchor);
if ((inOrder.start <= check.end) || (inOrder.end >= check.start)) {
SelectionSegment portion = check;
if (portion.start < inOrder.start)
portion.start = inOrder.start;
if (portion.end > inOrder.end)
portion.end = inOrder.end;
if (portion.start > portion.end)
return SelectionSegment();
else
return portion;
} else {
return SelectionSegment();
}
}
void SelectionRange::Swap() {
std::swap(caret, anchor);
}
bool SelectionRange::Trim(SelectionRange range) {
const SelectionPosition startRange = range.Start();
const SelectionPosition endRange = range.End();
SelectionPosition start = Start();
SelectionPosition end = End();
PLATFORM_ASSERT(start <= end);
PLATFORM_ASSERT(startRange <= endRange);
if ((startRange <= end) && (endRange >= start)) {
if ((start > startRange) && (end < endRange)) {
// Completely covered by range -> empty at start
end = start;
} else if ((start < startRange) && (end > endRange)) {
// Completely covers range -> empty at start
end = start;
} else if (start <= startRange) {
// Trim end
end = startRange;
} else { //
PLATFORM_ASSERT(end >= endRange);
// Trim start
start = endRange;
}
if (anchor > caret) {
caret = start;
anchor = end;
} else {
anchor = start;
caret = end;
}
return Empty();
} else {
return false;
}
}
// If range is all virtual collapse to start of virtual space
void SelectionRange::MinimizeVirtualSpace() {
if (caret.Position() == anchor.Position()) {
Sci::Position virtualSpace = caret.VirtualSpace();
if (virtualSpace > anchor.VirtualSpace())
virtualSpace = anchor.VirtualSpace();
caret.SetVirtualSpace(virtualSpace);
anchor.SetVirtualSpace(virtualSpace);
}
}
Selection::Selection() : mainRange(0), moveExtends(false), tentativeMain(false), selType(selStream) {
AddSelection(SelectionRange(SelectionPosition(0)));
}
Selection::~Selection() {
}
bool Selection::IsRectangular() const {
return (selType == selRectangle) || (selType == selThin);
}
Sci::Position Selection::MainCaret() const {
return ranges[mainRange].caret.Position();
}
Sci::Position Selection::MainAnchor() const {
return ranges[mainRange].anchor.Position();
}
SelectionRange &Selection::Rectangular() {
return rangeRectangular;
}
SelectionSegment Selection::Limits() const {
if (ranges.empty()) {
return SelectionSegment();
} else {
SelectionSegment sr(ranges[0].anchor, ranges[0].caret);
for (size_t i=1; i<ranges.size(); i++) {
sr.Extend(ranges[i].anchor);
sr.Extend(ranges[i].caret);
}
return sr;
}
}
SelectionSegment Selection::LimitsForRectangularElseMain() const {
if (IsRectangular()) {
return Limits();
} else {
return SelectionSegment(ranges[mainRange].caret, ranges[mainRange].anchor);
}
}
size_t Selection::Count() const {
return ranges.size();
}
size_t Selection::Main() const {
return mainRange;
}
void Selection::SetMain(size_t r) {
PLATFORM_ASSERT(r < ranges.size());
mainRange = r;
}
SelectionRange &Selection::Range(size_t r) {
return ranges[r];
}
const SelectionRange &Selection::Range(size_t r) const {
return ranges[r];
}
SelectionRange &Selection::RangeMain() {
return ranges[mainRange];
}
const SelectionRange &Selection::RangeMain() const {
return ranges[mainRange];
}
SelectionPosition Selection::Start() const {
if (IsRectangular()) {
return rangeRectangular.Start();
} else {
return ranges[mainRange].Start();
}
}
bool Selection::MoveExtends() const {
return moveExtends;
}
void Selection::SetMoveExtends(bool moveExtends_) {
moveExtends = moveExtends_;
}
bool Selection::Empty() const {
for (const SelectionRange &range : ranges) {
if (!range.Empty())
return false;
}
return true;
}
SelectionPosition Selection::Last() const {
SelectionPosition lastPosition;
for (const SelectionRange &range : ranges) {
if (lastPosition < range.caret)
lastPosition = range.caret;
if (lastPosition < range.anchor)
lastPosition = range.anchor;
}
return lastPosition;
}
Sci::Position Selection::Length() const {
Sci::Position len = 0;
for (const SelectionRange &range : ranges) {
len += range.Length();
}
return len;
}
void Selection::MovePositions(bool insertion, Sci::Position startChange, Sci::Position length) {
for (SelectionRange &range : ranges) {
range.MoveForInsertDelete(insertion, startChange, length);
}
if (selType == selRectangle) {
rangeRectangular.MoveForInsertDelete(insertion, startChange, length);
}
}
void Selection::TrimSelection(SelectionRange range) {
for (size_t i=0; i<ranges.size();) {
if ((i != mainRange) && (ranges[i].Trim(range))) {
// Trimmed to empty so remove
for (size_t j=i; j<ranges.size()-1; j++) {
ranges[j] = ranges[j+1];
if (j == mainRange-1)
mainRange--;
}
ranges.pop_back();
} else {
i++;
}
}
}
void Selection::TrimOtherSelections(size_t r, SelectionRange range) {
for (size_t i = 0; i<ranges.size(); ++i) {
if (i != r) {
ranges[i].Trim(range);
}
}
}
void Selection::SetSelection(SelectionRange range) {
ranges.clear();
ranges.push_back(range);
mainRange = ranges.size() - 1;
}
void Selection::AddSelection(SelectionRange range) {
TrimSelection(range);
ranges.push_back(range);
mainRange = ranges.size() - 1;
}
void Selection::AddSelectionWithoutTrim(SelectionRange range) {
ranges.push_back(range);
mainRange = ranges.size() - 1;
}
void Selection::DropSelection(size_t r) {
if ((ranges.size() > 1) && (r < ranges.size())) {
size_t mainNew = mainRange;
if (mainNew >= r) {
if (mainNew == 0) {
mainNew = ranges.size() - 2;
} else {
mainNew--;
}
}
ranges.erase(ranges.begin() + r);
mainRange = mainNew;
}
}
void Selection::DropAdditionalRanges() {
SetSelection(RangeMain());
}
void Selection::TentativeSelection(SelectionRange range) {
if (!tentativeMain) {
rangesSaved = ranges;
}
ranges = rangesSaved;
AddSelection(range);
TrimSelection(ranges[mainRange]);
tentativeMain = true;
}
void Selection::CommitTentative() {
rangesSaved.clear();
tentativeMain = false;
}
int Selection::CharacterInSelection(Sci::Position posCharacter) const {
for (size_t i=0; i<ranges.size(); i++) {
if (ranges[i].ContainsCharacter(posCharacter))
return i == mainRange ? 1 : 2;
}
return 0;
}
int Selection::InSelectionForEOL(Sci::Position pos) const {
for (size_t i=0; i<ranges.size(); i++) {
if (!ranges[i].Empty() && (pos > ranges[i].Start().Position()) && (pos <= ranges[i].End().Position()))
return i == mainRange ? 1 : 2;
}
return 0;
}
Sci::Position Selection::VirtualSpaceFor(Sci::Position pos) const {
Sci::Position virtualSpace = 0;
for (const SelectionRange &range : ranges) {
if ((range.caret.Position() == pos) && (virtualSpace < range.caret.VirtualSpace()))
virtualSpace = range.caret.VirtualSpace();
if ((range.anchor.Position() == pos) && (virtualSpace < range.anchor.VirtualSpace()))
virtualSpace = range.anchor.VirtualSpace();
}
return virtualSpace;
}
void Selection::Clear() {
ranges.clear();
ranges.emplace_back();
mainRange = ranges.size() - 1;
selType = selStream;
moveExtends = false;
ranges[mainRange].Reset();
rangeRectangular.Reset();
}
void Selection::RemoveDuplicates() {
for (size_t i=0; i<ranges.size()-1; i++) {
if (ranges[i].Empty()) {
size_t j=i+1;
while (j<ranges.size()) {
if (ranges[i] == ranges[j]) {
ranges.erase(ranges.begin() + j);
if (mainRange >= j)
mainRange--;
} else {
j++;
}
}
}
}
}
void Selection::RotateMain() {
mainRange = (mainRange + 1) % ranges.size();
}

View File

@@ -0,0 +1,192 @@
// Scintilla source code edit control
/** @file Selection.h
** Classes maintaining the selection.
**/
// Copyright 2009 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef SELECTION_H
#define SELECTION_H
namespace Scintilla {
class SelectionPosition {
Sci::Position position;
Sci::Position virtualSpace;
public:
explicit SelectionPosition(Sci::Position position_=INVALID_POSITION, Sci::Position virtualSpace_=0) : position(position_), virtualSpace(virtualSpace_) {
PLATFORM_ASSERT(virtualSpace < 800000);
if (virtualSpace < 0)
virtualSpace = 0;
}
void Reset() {
position = 0;
virtualSpace = 0;
}
void MoveForInsertDelete(bool insertion, Sci::Position startChange, Sci::Position length);
bool operator ==(const SelectionPosition &other) const {
return position == other.position && virtualSpace == other.virtualSpace;
}
bool operator <(const SelectionPosition &other) const;
bool operator >(const SelectionPosition &other) const;
bool operator <=(const SelectionPosition &other) const;
bool operator >=(const SelectionPosition &other) const;
Sci::Position Position() const {
return position;
}
void SetPosition(Sci::Position position_) {
position = position_;
virtualSpace = 0;
}
Sci::Position VirtualSpace() const {
return virtualSpace;
}
void SetVirtualSpace(Sci::Position virtualSpace_) {
PLATFORM_ASSERT(virtualSpace_ < 800000);
if (virtualSpace_ >= 0)
virtualSpace = virtualSpace_;
}
void Add(Sci::Position increment) {
position = position + increment;
}
bool IsValid() const {
return position >= 0;
}
};
// Ordered range to make drawing simpler
struct SelectionSegment {
SelectionPosition start;
SelectionPosition end;
SelectionSegment() : start(), end() {
}
SelectionSegment(SelectionPosition a, SelectionPosition b) {
if (a < b) {
start = a;
end = b;
} else {
start = b;
end = a;
}
}
bool Empty() const {
return start == end;
}
void Extend(SelectionPosition p) {
if (start > p)
start = p;
if (end < p)
end = p;
}
};
struct SelectionRange {
SelectionPosition caret;
SelectionPosition anchor;
SelectionRange() : caret(), anchor() {
}
explicit SelectionRange(SelectionPosition single) : caret(single), anchor(single) {
}
explicit SelectionRange(Sci::Position single) : caret(single), anchor(single) {
}
SelectionRange(SelectionPosition caret_, SelectionPosition anchor_) : caret(caret_), anchor(anchor_) {
}
SelectionRange(Sci::Position caret_, Sci::Position anchor_) : caret(caret_), anchor(anchor_) {
}
bool Empty() const {
return anchor == caret;
}
Sci::Position Length() const;
// Sci::Position Width() const; // Like Length but takes virtual space into account
bool operator ==(const SelectionRange &other) const {
return caret == other.caret && anchor == other.anchor;
}
bool operator <(const SelectionRange &other) const {
return caret < other.caret || ((caret == other.caret) && (anchor < other.anchor));
}
void Reset() {
anchor.Reset();
caret.Reset();
}
void ClearVirtualSpace() {
anchor.SetVirtualSpace(0);
caret.SetVirtualSpace(0);
}
void MoveForInsertDelete(bool insertion, Sci::Position startChange, Sci::Position length);
bool Contains(Sci::Position pos) const;
bool Contains(SelectionPosition sp) const;
bool ContainsCharacter(Sci::Position posCharacter) const;
SelectionSegment Intersect(SelectionSegment check) const;
SelectionPosition Start() const {
return (anchor < caret) ? anchor : caret;
}
SelectionPosition End() const {
return (anchor < caret) ? caret : anchor;
}
void Swap();
bool Trim(SelectionRange range);
// If range is all virtual collapse to start of virtual space
void MinimizeVirtualSpace();
};
class Selection {
std::vector<SelectionRange> ranges;
std::vector<SelectionRange> rangesSaved;
SelectionRange rangeRectangular;
size_t mainRange;
bool moveExtends;
bool tentativeMain;
public:
enum selTypes { noSel, selStream, selRectangle, selLines, selThin };
selTypes selType;
Selection();
~Selection();
bool IsRectangular() const;
Sci::Position MainCaret() const;
Sci::Position MainAnchor() const;
SelectionRange &Rectangular();
SelectionSegment Limits() const;
// This is for when you want to move the caret in response to a
// user direction command - for rectangular selections, use the range
// that covers all selected text otherwise return the main selection.
SelectionSegment LimitsForRectangularElseMain() const;
size_t Count() const;
size_t Main() const;
void SetMain(size_t r);
SelectionRange &Range(size_t r);
const SelectionRange &Range(size_t r) const;
SelectionRange &RangeMain();
const SelectionRange &RangeMain() const;
SelectionPosition Start() const;
bool MoveExtends() const;
void SetMoveExtends(bool moveExtends_);
bool Empty() const;
SelectionPosition Last() const;
Sci::Position Length() const;
void MovePositions(bool insertion, Sci::Position startChange, Sci::Position length);
void TrimSelection(SelectionRange range);
void TrimOtherSelections(size_t r, SelectionRange range);
void SetSelection(SelectionRange range);
void AddSelection(SelectionRange range);
void AddSelectionWithoutTrim(SelectionRange range);
void DropSelection(size_t r);
void DropAdditionalRanges();
void TentativeSelection(SelectionRange range);
void CommitTentative();
int CharacterInSelection(Sci::Position posCharacter) const;
int InSelectionForEOL(Sci::Position pos) const;
Sci::Position VirtualSpaceFor(Sci::Position pos) const;
void Clear();
void RemoveDuplicates();
void RotateMain();
bool Tentative() const { return tentativeMain; }
std::vector<SelectionRange> RangesCopy() const {
return ranges;
}
};
}
#endif

View File

@@ -0,0 +1,156 @@
// Scintilla source code edit control
/** @file SparseVector.h
** Hold data sparsely associated with elements in a range.
**/
// Copyright 2016 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef SPARSEVECTOR_H
#define SPARSEVECTOR_H
namespace Scintilla {
// SparseVector is similar to RunStyles but is more efficient for cases where values occur
// for one position instead of over a range of positions.
template <typename T>
class SparseVector {
private:
std::unique_ptr<Partitioning<Sci::Position>> starts;
std::unique_ptr<SplitVector<T>> values;
T empty;
void ClearValue(Sci::Position partition) {
values->SetValueAt(partition, T());
}
public:
SparseVector() : empty() {
starts = std::unique_ptr<Partitioning<Sci::Position>>(new Partitioning<Sci::Position>(8));
values = std::unique_ptr<SplitVector<T>>(new SplitVector<T>());
values->InsertEmpty(0, 2);
}
// Deleted so SparseVector objects can not be copied.
SparseVector(const SparseVector &) = delete;
SparseVector(SparseVector &&) = delete;
void operator=(const SparseVector &) = delete;
void operator=(SparseVector &&) = delete;
~SparseVector() {
starts.reset();
// starts dead here but not used by ClearValue.
for (Sci::Position part = 0; part < values->Length(); part++) {
ClearValue(part);
}
values.reset();
}
Sci::Position Length() const {
return starts->PositionFromPartition(starts->Partitions());
}
Sci::Position Elements() const {
return starts->Partitions();
}
Sci::Position PositionOfElement(int element) const {
return starts->PositionFromPartition(element);
}
const T& ValueAt(Sci::Position position) const {
assert(position < Length());
const Sci::Position partition = starts->PartitionFromPosition(position);
const Sci::Position startPartition = starts->PositionFromPartition(partition);
if (startPartition == position) {
return values->ValueAt(partition);
} else {
return empty;
}
}
template <typename ParamType>
void SetValueAt(Sci::Position position, ParamType &&value) {
assert(position < Length());
const Sci::Position partition = starts->PartitionFromPosition(position);
const Sci::Position startPartition = starts->PositionFromPartition(partition);
if (value == T()) {
// Setting the empty value is equivalent to deleting the position
if (position == 0) {
ClearValue(partition);
} else if (position == startPartition) {
// Currently an element at this position, so remove
ClearValue(partition);
starts->RemovePartition(partition);
values->Delete(partition);
}
// Else element remains empty
} else {
if (position == startPartition) {
// Already a value at this position, so replace
ClearValue(partition);
values->SetValueAt(partition, std::forward<ParamType>(value));
} else {
// Insert a new element
starts->InsertPartition(partition + 1, position);
values->Insert(partition + 1, std::forward<ParamType>(value));
}
}
}
void InsertSpace(Sci::Position position, Sci::Position insertLength) {
assert(position <= Length()); // Only operation that works at end.
const Sci::Position partition = starts->PartitionFromPosition(position);
const Sci::Position startPartition = starts->PositionFromPartition(partition);
if (startPartition == position) {
const bool positionOccupied = values->ValueAt(partition) != T();
// Inserting at start of run so make previous longer
if (partition == 0) {
// Inserting at start of document so ensure start empty
if (positionOccupied) {
starts->InsertPartition(1, 0);
values->InsertEmpty(0, 1);
}
starts->InsertText(partition, insertLength);
} else {
if (positionOccupied) {
starts->InsertText(partition - 1, insertLength);
} else {
// Insert at end of run so do not extend style
starts->InsertText(partition, insertLength);
}
}
} else {
starts->InsertText(partition, insertLength);
}
}
void DeletePosition(Sci::Position position) {
assert(position < Length());
Sci::Position partition = starts->PartitionFromPosition(position);
const Sci::Position startPartition = starts->PositionFromPartition(partition);
if (startPartition == position) {
if (partition == 0) {
ClearValue(0);
} else if (partition == starts->Partitions()) {
// This should not be possible
ClearValue(partition);
throw std::runtime_error("SparseVector: deleting end partition.");
} else {
ClearValue(partition);
starts->RemovePartition(partition);
values->Delete(partition);
// Its the previous partition now that gets smaller
partition--;
}
}
starts->InsertText(partition, -1);
}
void Check() const {
if (Length() < 0) {
throw std::runtime_error("SparseVector: Length can not be negative.");
}
if (starts->Partitions() < 1) {
throw std::runtime_error("SparseVector: Must always have 1 or more partitions.");
}
if (starts->Partitions() != values->Length() - 1) {
throw std::runtime_error("SparseVector: Partitions and values different lengths.");
}
// The final element can not be set
if (values->ValueAt(values->Length() - 1) != T()) {
throw std::runtime_error("SparseVector: Unused style at end changed.");
}
}
};
}
#endif

View File

@@ -0,0 +1,332 @@
// Scintilla source code edit control
/** @file SplitVector.h
** Main data structure for holding arrays that handle insertions
** and deletions efficiently.
**/
// Copyright 1998-2007 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef SPLITVECTOR_H
#define SPLITVECTOR_H
namespace Scintilla {
template <typename T>
class SplitVector {
protected:
std::vector<T> body;
T empty; /// Returned as the result of out-of-bounds access.
ptrdiff_t lengthBody;
ptrdiff_t part1Length;
ptrdiff_t gapLength; /// invariant: gapLength == body.size() - lengthBody
ptrdiff_t growSize;
/// Move the gap to a particular position so that insertion and
/// deletion at that point will not require much copying and
/// hence be fast.
void GapTo(ptrdiff_t position) noexcept {
if (position != part1Length) {
if (position < part1Length) {
// Moving the gap towards start so moving elements towards end
std::move_backward(
body.data() + position,
body.data() + part1Length,
body.data() + gapLength + part1Length);
} else { // position > part1Length
// Moving the gap towards end so moving elements towards start
std::move(
body.data() + part1Length + gapLength,
body.data() + gapLength + position,
body.data() + part1Length);
}
part1Length = position;
}
}
/// Check that there is room in the buffer for an insertion,
/// reallocating if more space needed.
void RoomFor(ptrdiff_t insertionLength) {
if (gapLength <= insertionLength) {
while (growSize < static_cast<ptrdiff_t>(body.size() / 6))
growSize *= 2;
ReAllocate(body.size() + insertionLength + growSize);
}
}
void Init() {
body.clear();
body.shrink_to_fit();
lengthBody = 0;
part1Length = 0;
gapLength = 0;
growSize = 8;
}
public:
/// Construct a split buffer.
SplitVector() : empty(), lengthBody(0), part1Length(0), gapLength(0), growSize(8) {
}
// Deleted so SplitVector objects can not be copied.
SplitVector(const SplitVector &) = delete;
SplitVector(SplitVector &&) = delete;
void operator=(const SplitVector &) = delete;
void operator=(SplitVector &&) = delete;
~SplitVector() {
}
ptrdiff_t GetGrowSize() const {
return growSize;
}
void SetGrowSize(ptrdiff_t growSize_) {
growSize = growSize_;
}
/// Reallocate the storage for the buffer to be newSize and
/// copy exisiting contents to the new buffer.
/// Must not be used to decrease the size of the buffer.
void ReAllocate(ptrdiff_t newSize) {
if (newSize < 0)
throw std::runtime_error("SplitVector::ReAllocate: negative size.");
if (newSize > static_cast<ptrdiff_t>(body.size())) {
// Move the gap to the end
GapTo(lengthBody);
gapLength += newSize - static_cast<ptrdiff_t>(body.size());
// RoomFor implements a growth strategy but so does vector::resize so
// ensure vector::resize allocates exactly the amount wanted by
// calling reserve first.
body.reserve(newSize);
body.resize(newSize);
}
}
/// Retrieve the element at a particular position.
/// Retrieving positions outside the range of the buffer returns empty or 0.
const T& ValueAt(ptrdiff_t position) const noexcept {
if (position < part1Length) {
if (position < 0) {
return empty;
} else {
return body[position];
}
} else {
if (position >= lengthBody) {
return empty;
} else {
return body[gapLength + position];
}
}
}
/// Set the element at a particular position.
/// Setting positions outside the range of the buffer performs no assignment
/// but asserts in debug builds.
template <typename ParamType>
void SetValueAt(ptrdiff_t position, ParamType&& v) noexcept {
if (position < part1Length) {
PLATFORM_ASSERT(position >= 0);
if (position < 0) {
;
} else {
body[position] = std::forward<ParamType>(v);
}
} else {
PLATFORM_ASSERT(position < lengthBody);
if (position >= lengthBody) {
;
} else {
body[gapLength + position] = std::forward<ParamType>(v);
}
}
}
/// Retrieve the element at a particular position.
/// The position must be within bounds or an assertion is triggered.
const T &operator[](ptrdiff_t position) const noexcept {
PLATFORM_ASSERT(position >= 0 && position < lengthBody);
if (position < part1Length) {
return body[position];
} else {
return body[gapLength + position];
}
}
/// Retrieve reference to the element at a particular position.
/// This, instead of the const variant, can be used to mutate in-place.
/// The position must be within bounds or an assertion is triggered.
T &operator[](ptrdiff_t position) noexcept {
PLATFORM_ASSERT(position >= 0 && position < lengthBody);
if (position < part1Length) {
return body[position];
} else {
return body[gapLength + position];
}
}
/// Retrieve the length of the buffer.
ptrdiff_t Length() const noexcept {
return lengthBody;
}
/// Insert a single value into the buffer.
/// Inserting at positions outside the current range fails.
void Insert(ptrdiff_t position, T v) {
PLATFORM_ASSERT((position >= 0) && (position <= lengthBody));
if ((position < 0) || (position > lengthBody)) {
return;
}
RoomFor(1);
GapTo(position);
body[part1Length] = std::move(v);
lengthBody++;
part1Length++;
gapLength--;
}
/// Insert a number of elements into the buffer setting their value.
/// Inserting at positions outside the current range fails.
void InsertValue(ptrdiff_t position, ptrdiff_t insertLength, T v) {
PLATFORM_ASSERT((position >= 0) && (position <= lengthBody));
if (insertLength > 0) {
if ((position < 0) || (position > lengthBody)) {
return;
}
RoomFor(insertLength);
GapTo(position);
std::fill(body.data() + part1Length, body.data() + part1Length + insertLength, v);
lengthBody += insertLength;
part1Length += insertLength;
gapLength -= insertLength;
}
}
/// Add some new empty elements.
/// InsertValue is good for value objects but not for unique_ptr objects
/// since they can only be moved from once.
void InsertEmpty(ptrdiff_t position, ptrdiff_t insertLength) {
PLATFORM_ASSERT((position >= 0) && (position <= lengthBody));
if (insertLength > 0) {
if ((position < 0) || (position > lengthBody)) {
return;
}
RoomFor(insertLength);
GapTo(position);
for (ptrdiff_t elem = part1Length; elem < part1Length + insertLength; elem++) {
T emptyOne = {};
body[elem] = std::move(emptyOne);
}
lengthBody += insertLength;
part1Length += insertLength;
gapLength -= insertLength;
}
}
/// Ensure at least length elements allocated,
/// appending zero valued elements if needed.
void EnsureLength(ptrdiff_t wantedLength) {
if (Length() < wantedLength) {
InsertEmpty(Length(), wantedLength - Length());
}
}
/// Insert text into the buffer from an array.
void InsertFromArray(ptrdiff_t positionToInsert, const T s[], ptrdiff_t positionFrom, ptrdiff_t insertLength) {
PLATFORM_ASSERT((positionToInsert >= 0) && (positionToInsert <= lengthBody));
if (insertLength > 0) {
if ((positionToInsert < 0) || (positionToInsert > lengthBody)) {
return;
}
RoomFor(insertLength);
GapTo(positionToInsert);
std::copy(s + positionFrom, s + positionFrom + insertLength, body.data() + part1Length);
lengthBody += insertLength;
part1Length += insertLength;
gapLength -= insertLength;
}
}
/// Delete one element from the buffer.
void Delete(ptrdiff_t position) {
PLATFORM_ASSERT((position >= 0) && (position < lengthBody));
DeleteRange(position, 1);
}
/// Delete a range from the buffer.
/// Deleting positions outside the current range fails.
/// Cannot be noexcept as vector::shrink_to_fit may be called and it may throw.
void DeleteRange(ptrdiff_t position, ptrdiff_t deleteLength) {
PLATFORM_ASSERT((position >= 0) && (position + deleteLength <= lengthBody));
if ((position < 0) || ((position + deleteLength) > lengthBody)) {
return;
}
if ((position == 0) && (deleteLength == lengthBody)) {
// Full deallocation returns storage and is faster
Init();
} else if (deleteLength > 0) {
GapTo(position);
lengthBody -= deleteLength;
gapLength += deleteLength;
}
}
/// Delete all the buffer contents.
void DeleteAll() {
DeleteRange(0, lengthBody);
}
/// Retrieve a range of elements into an array
void GetRange(T *buffer, ptrdiff_t position, ptrdiff_t retrieveLength) const noexcept {
// Split into up to 2 ranges, before and after the split then use memcpy on each.
ptrdiff_t range1Length = 0;
if (position < part1Length) {
const ptrdiff_t part1AfterPosition = part1Length - position;
range1Length = retrieveLength;
if (range1Length > part1AfterPosition)
range1Length = part1AfterPosition;
}
std::copy(body.data() + position, body.data() + position + range1Length, buffer);
buffer += range1Length;
position = position + range1Length + gapLength;
ptrdiff_t range2Length = retrieveLength - range1Length;
std::copy(body.data() + position, body.data() + position + range2Length, buffer);
}
/// Compact the buffer and return a pointer to the first element.
/// Also ensures there is an empty element beyond logical end in case its
/// passed to a function expecting a NUL terminated string.
T *BufferPointer() {
RoomFor(1);
GapTo(lengthBody);
T emptyOne = {};
body[lengthBody] = std::move(emptyOne);
return body.data();
}
/// Return a pointer to a range of elements, first rearranging the buffer if
/// needed to make that range contiguous.
T *RangePointer(ptrdiff_t position, ptrdiff_t rangeLength) noexcept {
if (position < part1Length) {
if ((position + rangeLength) > part1Length) {
// Range overlaps gap, so move gap to start of range.
GapTo(position);
return body.data() + position + gapLength;
} else {
return body.data() + position;
}
} else {
return body.data() + position + gapLength;
}
}
/// Return the position of the gap within the buffer.
ptrdiff_t GapPosition() const noexcept {
return part1Length;
}
};
}
#endif

View File

@@ -0,0 +1,168 @@
// 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 <stdexcept>
#include <vector>
#include <memory>
#include "Platform.h"
#include "Scintilla.h"
#include "Style.h"
using namespace Scintilla;
FontAlias::FontAlias() {
}
FontAlias::FontAlias(const FontAlias &other) : Font() {
SetID(other.fid);
}
FontAlias::~FontAlias() {
SetID(0);
// ~Font will not release the actual font resource since it is now 0
}
void FontAlias::MakeAlias(const Font &fontOrigin) {
SetID(fontOrigin.GetID());
}
void FontAlias::ClearFont() {
SetID(0);
}
bool FontSpecification::operator==(const FontSpecification &other) const {
return fontName == other.fontName &&
weight == other.weight &&
italic == other.italic &&
size == other.size &&
characterSet == other.characterSet &&
extraFontFlag == other.extraFontFlag;
}
bool FontSpecification::operator<(const FontSpecification &other) const {
if (fontName != other.fontName)
return fontName < other.fontName;
if (weight != other.weight)
return weight < other.weight;
if (italic != other.italic)
return italic == false;
if (size != other.size)
return size < other.size;
if (characterSet != other.characterSet)
return characterSet < other.characterSet;
if (extraFontFlag != other.extraFontFlag)
return extraFontFlag < other.extraFontFlag;
return false;
}
FontMeasurements::FontMeasurements() {
ClearMeasurements();
}
void FontMeasurements::ClearMeasurements() {
ascent = 1;
descent = 1;
capitalHeight = 1;
aveCharWidth = 1;
spaceWidth = 1;
sizeZoomed = 2;
}
Style::Style() : FontSpecification() {
Clear(ColourDesired(0, 0, 0), ColourDesired(0xff, 0xff, 0xff),
Platform::DefaultFontSize() * SC_FONT_SIZE_MULTIPLIER, nullptr, SC_CHARSET_DEFAULT,
SC_WEIGHT_NORMAL, false, false, false, caseMixed, true, true, false);
}
Style::Style(const Style &source) : FontSpecification(), FontMeasurements() {
Clear(ColourDesired(0, 0, 0), ColourDesired(0xff, 0xff, 0xff),
0, nullptr, 0,
SC_WEIGHT_NORMAL, false, false, false, caseMixed, true, true, false);
fore = source.fore;
back = source.back;
characterSet = source.characterSet;
weight = source.weight;
italic = source.italic;
size = source.size;
fontName = source.fontName;
eolFilled = source.eolFilled;
underline = source.underline;
caseForce = source.caseForce;
visible = source.visible;
changeable = source.changeable;
hotspot = source.hotspot;
}
Style::~Style() {
}
Style &Style::operator=(const Style &source) {
if (this == &source)
return * this;
Clear(ColourDesired(0, 0, 0), ColourDesired(0xff, 0xff, 0xff),
0, nullptr, SC_CHARSET_DEFAULT,
SC_WEIGHT_NORMAL, false, false, false, caseMixed, true, true, false);
fore = source.fore;
back = source.back;
characterSet = source.characterSet;
weight = source.weight;
italic = source.italic;
size = source.size;
fontName = source.fontName;
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_,
int weight_, bool italic_, bool eolFilled_,
bool underline_, ecaseForced caseForce_,
bool visible_, bool changeable_, bool hotspot_) {
fore = fore_;
back = back_;
characterSet = characterSet_;
weight = weight_;
italic = italic_;
size = size_;
fontName = fontName_;
eolFilled = eolFilled_;
underline = underline_;
caseForce = caseForce_;
visible = visible_;
changeable = changeable_;
hotspot = hotspot_;
font.ClearFont();
FontMeasurements::ClearMeasurements();
}
void Style::ClearTo(const Style &source) {
Clear(
source.fore,
source.back,
source.size,
source.fontName,
source.characterSet,
source.weight,
source.italic,
source.eolFilled,
source.underline,
source.caseForce,
source.visible,
source.changeable,
source.hotspot);
}
void Style::Copy(Font &font_, const FontMeasurements &fm_) {
font.MakeAlias(font_);
(FontMeasurements &)(*this) = fm_;
}

View File

@@ -0,0 +1,92 @@
// 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
namespace Scintilla {
struct FontSpecification {
const char *fontName;
int weight;
bool italic;
int size;
int characterSet;
int extraFontFlag;
FontSpecification() :
fontName(nullptr),
weight(SC_WEIGHT_NORMAL),
italic(false),
size(10 * SC_FONT_SIZE_MULTIPLIER),
characterSet(0),
extraFontFlag(0) {
}
bool operator==(const FontSpecification &other) const;
bool operator<(const FontSpecification &other) const;
};
// Just like Font but only has a copy of the FontID so should not delete it
class FontAlias : public Font {
public:
FontAlias();
// FontAlias objects can not be assigned except for initialization
FontAlias(const FontAlias &);
FontAlias(FontAlias &&) = delete;
FontAlias &operator=(const FontAlias &) = delete;
FontAlias &operator=(FontAlias &&) = delete;
~FontAlias() override;
void MakeAlias(const Font &fontOrigin);
void ClearFont();
};
struct FontMeasurements {
unsigned int ascent;
unsigned int descent;
XYPOSITION capitalHeight; // Top of capital letter to baseline: ascent - internal leading
XYPOSITION aveCharWidth;
XYPOSITION spaceWidth;
int sizeZoomed;
FontMeasurements();
void ClearMeasurements();
};
/**
*/
class Style : public FontSpecification, public FontMeasurements {
public:
ColourDesired fore;
ColourDesired back;
bool eolFilled;
bool underline;
enum ecaseForced {caseMixed, caseUpper, caseLower, caseCamel};
ecaseForced caseForce;
bool visible;
bool changeable;
bool hotspot;
FontAlias font;
Style();
Style(const Style &source);
Style(Style &&) = default;
~Style();
Style &operator=(const Style &source);
Style &operator=(Style &&) = delete;
void Clear(ColourDesired fore_, ColourDesired back_,
int size_,
const char *fontName_, int characterSet_,
int weight_, bool italic_, bool eolFilled_,
bool underline_, ecaseForced caseForce_,
bool visible_, bool changeable_, bool hotspot_);
void ClearTo(const Style &source);
void Copy(Font &font_, const FontMeasurements &fm_);
bool IsProtected() const { return !(changeable && visible);}
};
}
#endif

View File

@@ -0,0 +1,368 @@
// Scintilla source code edit control
/** @file UniConversion.cxx
** Functions to handle UTF-8 and UTF-16 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 <cstdlib>
#include <stdexcept>
#include <string>
#include "UniConversion.h"
using namespace Scintilla;
namespace Scintilla {
size_t UTF8Length(const wchar_t *uptr, size_t tlen) {
size_t len = 0;
for (size_t i = 0; i < tlen && uptr[i];) {
const unsigned int uch = uptr[i];
if (uch < 0x80) {
len++;
} else if (uch < 0x800) {
len += 2;
} else if ((uch >= SURROGATE_LEAD_FIRST) &&
(uch <= SURROGATE_TRAIL_LAST)) {
len += 4;
i++;
} else {
len += 3;
}
i++;
}
return len;
}
void UTF8FromUTF16(const wchar_t *uptr, size_t tlen, char *putf, size_t len) {
size_t k = 0;
for (size_t i = 0; i < tlen && uptr[i];) {
const 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 if ((uch >= SURROGATE_LEAD_FIRST) &&
(uch <= SURROGATE_TRAIL_LAST)) {
// Half a surrogate pair
i++;
const unsigned int xch = 0x10000 + ((uch & 0x3ff) << 10) + (uptr[i] & 0x3ff);
putf[k++] = static_cast<char>(0xF0 | (xch >> 18));
putf[k++] = static_cast<char>(0x80 | ((xch >> 12) & 0x3f));
putf[k++] = static_cast<char>(0x80 | ((xch >> 6) & 0x3f));
putf[k++] = static_cast<char>(0x80 | (xch & 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));
}
i++;
}
if (k < len)
putf[k] = '\0';
}
void UTF8FromUTF32Character(int uch, char *putf) {
size_t k = 0;
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 if (uch < 0x10000) {
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));
} else {
putf[k++] = static_cast<char>(0xF0 | (uch >> 18));
putf[k++] = static_cast<char>(0x80 | ((uch >> 12) & 0x3f));
putf[k++] = static_cast<char>(0x80 | ((uch >> 6) & 0x3f));
putf[k++] = static_cast<char>(0x80 | (uch & 0x3f));
}
putf[k] = '\0';
}
size_t UTF16Length(const char *s, size_t len) {
size_t ulen = 0;
for (size_t i = 0; i < len;) {
const unsigned char ch = s[i];
const unsigned int byteCount = UTF8BytesOfLead[ch];
const unsigned int utf16Len = UTF16LengthFromUTF8ByteCount(byteCount);
i += byteCount;
ulen += (i > len) ? 1 : utf16Len;
}
return ulen;
}
constexpr unsigned char TrailByteValue(unsigned char c) {
// The top 2 bits are 0b10 to indicate a trail byte.
// The lower 6 bits contain the value.
return c & 0x3F;
}
size_t UTF16FromUTF8(const char *s, size_t len, wchar_t *tbuf, size_t tlen) {
size_t ui = 0;
for (size_t i = 0; i < len;) {
unsigned char ch = s[i];
const unsigned int byteCount = UTF8BytesOfLead[ch];
unsigned int value;
if (i + byteCount > len) {
// Trying to read past end but still have space to write
if (ui < tlen) {
tbuf[ui] = ch;
ui++;
}
break;
}
const size_t outLen = UTF16LengthFromUTF8ByteCount(byteCount);
if (ui + outLen > tlen) {
throw std::runtime_error("UTF16FromUTF8: attempted write beyond end");
}
i++;
switch (byteCount) {
case 1:
tbuf[ui] = ch;
break;
case 2:
value = (ch & 0x1F) << 6;
ch = s[i++];
value += TrailByteValue(ch);
tbuf[ui] = static_cast<wchar_t>(value);
break;
case 3:
value = (ch & 0xF) << 12;
ch = s[i++];
value += (TrailByteValue(ch) << 6);
ch = s[i++];
value += TrailByteValue(ch);
tbuf[ui] = static_cast<wchar_t>(value);
break;
default:
// Outside the BMP so need two surrogates
value = (ch & 0x7) << 18;
ch = s[i++];
value += TrailByteValue(ch) << 12;
ch = s[i++];
value += TrailByteValue(ch) << 6;
ch = s[i++];
value += TrailByteValue(ch);
tbuf[ui] = static_cast<wchar_t>(((value - 0x10000) >> 10) + SURROGATE_LEAD_FIRST);
ui++;
tbuf[ui] = static_cast<wchar_t>((value & 0x3ff) + SURROGATE_TRAIL_FIRST);
break;
}
ui++;
}
return ui;
}
size_t UTF32FromUTF8(const char *s, size_t len, unsigned int *tbuf, size_t tlen) {
size_t ui = 0;
for (size_t i = 0; i < len;) {
unsigned char ch = s[i];
const unsigned int byteCount = UTF8BytesOfLead[ch];
unsigned int value;
if (i + byteCount > len) {
// Trying to read past end but still have space to write
if (ui < tlen) {
tbuf[ui] = ch;
ui++;
}
break;
}
if (ui == tlen) {
throw std::runtime_error("UTF32FromUTF8: attempted write beyond end");
}
i++;
switch (byteCount) {
case 1:
value = ch;
break;
case 2:
value = (ch & 0x1F) << 6;
ch = s[i++];
value += TrailByteValue(ch);
break;
case 3:
value = (ch & 0xF) << 12;
ch = s[i++];
value += TrailByteValue(ch) << 6;
ch = s[i++];
value += TrailByteValue(ch);
break;
default:
value = (ch & 0x7) << 18;
ch = s[i++];
value += TrailByteValue(ch) << 12;
ch = s[i++];
value += TrailByteValue(ch) << 6;
ch = s[i++];
value += TrailByteValue(ch);
break;
}
tbuf[ui] = value;
ui++;
}
return ui;
}
unsigned int UTF16FromUTF32Character(unsigned int val, wchar_t *tbuf) noexcept {
if (val < SUPPLEMENTAL_PLANE_FIRST) {
tbuf[0] = static_cast<wchar_t>(val);
return 1;
} else {
tbuf[0] = static_cast<wchar_t>(((val - SUPPLEMENTAL_PLANE_FIRST) >> 10) + SURROGATE_LEAD_FIRST);
tbuf[1] = static_cast<wchar_t>((val & 0x3ff) + SURROGATE_TRAIL_FIRST);
return 2;
}
}
const unsigned char UTF8BytesOfLead[256] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 00 - 0F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 10 - 1F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 20 - 2F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 30 - 3F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 40 - 4F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 50 - 5F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 60 - 6F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 70 - 7F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80 - 8F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 90 - 9F
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A0 - AF
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B0 - BF
1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0 - CF
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // D0 - DF
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // E0 - EF
4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // F0 - FF
};
// Return both the width of the first character in the string and a status
// saying whether it is valid or invalid.
// Most invalid sequences return a width of 1 so are treated as isolated bytes but
// the non-characters *FFFE, *FFFF and FDD0 .. FDEF return 3 or 4 as they can be
// reasonably treated as code points in some circumstances. They will, however,
// not have associated glyphs.
int UTF8Classify(const unsigned char *us, size_t len) noexcept {
// For the rules: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
if (us[0] < 0x80) {
// ASCII
return 1;
}
const size_t byteCount = UTF8BytesOfLead[us[0]];
if (byteCount == 1 || byteCount > len) {
// Invalid lead byte
return UTF8MaskInvalid | 1;
}
if (!UTF8IsTrailByte(us[1])) {
// Invalid trail byte
return UTF8MaskInvalid | 1;
}
switch (byteCount) {
case 2:
return 2;
case 3:
if (UTF8IsTrailByte(us[2])) {
if ((*us == 0xe0) && ((us[1] & 0xe0) == 0x80)) {
// Overlong
return UTF8MaskInvalid | 1;
}
if ((*us == 0xed) && ((us[1] & 0xe0) == 0xa0)) {
// Surrogate
return UTF8MaskInvalid | 1;
}
if ((*us == 0xef) && (us[1] == 0xbf) && (us[2] == 0xbe)) {
// U+FFFE non-character - 3 bytes long
return UTF8MaskInvalid | 3;
}
if ((*us == 0xef) && (us[1] == 0xbf) && (us[2] == 0xbf)) {
// U+FFFF non-character - 3 bytes long
return UTF8MaskInvalid | 3;
}
if ((*us == 0xef) && (us[1] == 0xb7) && (((us[2] & 0xf0) == 0x90) || ((us[2] & 0xf0) == 0xa0))) {
// U+FDD0 .. U+FDEF
return UTF8MaskInvalid | 3;
}
return 3;
}
break;
default:
if (UTF8IsTrailByte(us[2]) && UTF8IsTrailByte(us[3])) {
if (((us[1] & 0xf) == 0xf) && (us[2] == 0xbf) && ((us[3] == 0xbe) || (us[3] == 0xbf))) {
// *FFFE or *FFFF non-character
return UTF8MaskInvalid | 4;
}
if (*us == 0xf4) {
// Check if encoding a value beyond the last Unicode character 10FFFF
if (us[1] > 0x8f) {
return UTF8MaskInvalid | 1;
}
} else if ((*us == 0xf0) && ((us[1] & 0xf0) == 0x80)) {
// Overlong
return UTF8MaskInvalid | 1;
}
return 4;
}
break;
}
return UTF8MaskInvalid | 1;
}
int UTF8DrawBytes(const unsigned char *us, int len) noexcept {
const int utf8StatusNext = UTF8Classify(us, len);
return (utf8StatusNext & UTF8MaskInvalid) ? 1 : (utf8StatusNext & UTF8MaskWidth);
}
bool UTF8IsValid(const char *s, size_t len) noexcept {
const unsigned char *us = reinterpret_cast<const unsigned char *>(s);
size_t remaining = len;
while (remaining > 0) {
const int utf8Status = UTF8Classify(us, remaining);
if (utf8Status & UTF8MaskInvalid) {
return false;
} else {
const int lenChar = utf8Status & UTF8MaskWidth;
us += lenChar;
remaining -= lenChar;
}
}
return remaining == 0;
}
// Replace invalid bytes in UTF-8 with the replacement character
std::string FixInvalidUTF8(const std::string &text) {
std::string result;
const char *s = text.c_str();
size_t remaining = text.size();
while (remaining > 0) {
const int utf8Status = UTF8Classify(reinterpret_cast<const unsigned char *>(s), remaining);
if (utf8Status & UTF8MaskInvalid) {
// Replacement character 0xFFFD = UTF8:"efbfbd".
result.append("\xef\xbf\xbd");
s++;
remaining--;
} else {
const size_t len = utf8Status & UTF8MaskWidth;
result.append(s, len);
s += len;
remaining -= len;
}
}
return result;
}
}

View File

@@ -0,0 +1,86 @@
// Scintilla source code edit control
/** @file UniConversion.h
** Functions to handle UTF-8 and UTF-16 strings.
**/
// Copyright 1998-2001 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef UNICONVERSION_H
#define UNICONVERSION_H
namespace Scintilla {
const int UTF8MaxBytes = 4;
const int unicodeReplacementChar = 0xFFFD;
size_t UTF8Length(const wchar_t *uptr, size_t tlen);
void UTF8FromUTF16(const wchar_t *uptr, size_t tlen, char *putf, size_t len);
void UTF8FromUTF32Character(int uch, char *putf);
size_t UTF16Length(const char *s, size_t len);
size_t UTF16FromUTF8(const char *s, size_t len, wchar_t *tbuf, size_t tlen);
size_t UTF32FromUTF8(const char *s, size_t len, unsigned int *tbuf, size_t tlen);
unsigned int UTF16FromUTF32Character(unsigned int val, wchar_t *tbuf) noexcept;
bool UTF8IsValid(const char *s, size_t len) noexcept;
std::string FixInvalidUTF8(const std::string &text);
extern const unsigned char UTF8BytesOfLead[256];
inline int UnicodeFromUTF8(const unsigned char *us) noexcept {
switch (UTF8BytesOfLead[us[0]]) {
case 1:
return us[0];
case 2:
return ((us[0] & 0x1F) << 6) + (us[1] & 0x3F);
case 3:
return ((us[0] & 0xF) << 12) + ((us[1] & 0x3F) << 6) + (us[2] & 0x3F);
default:
return ((us[0] & 0x7) << 18) + ((us[1] & 0x3F) << 12) + ((us[2] & 0x3F) << 6) + (us[3] & 0x3F);
}
}
inline constexpr bool UTF8IsTrailByte(unsigned char ch) noexcept {
return (ch >= 0x80) && (ch < 0xc0);
}
inline constexpr bool UTF8IsAscii(int ch) noexcept {
return ch < 0x80;
}
enum { UTF8MaskWidth=0x7, UTF8MaskInvalid=0x8 };
int UTF8Classify(const unsigned char *us, size_t len) noexcept;
// Similar to UTF8Classify but returns a length of 1 for invalid bytes
// instead of setting the invalid flag
int UTF8DrawBytes(const unsigned char *us, int len) noexcept;
// Line separator is U+2028 \xe2\x80\xa8
// Paragraph separator is U+2029 \xe2\x80\xa9
const int UTF8SeparatorLength = 3;
inline bool UTF8IsSeparator(const unsigned char *us) noexcept {
return (us[0] == 0xe2) && (us[1] == 0x80) && ((us[2] == 0xa8) || (us[2] == 0xa9));
}
// NEL is U+0085 \xc2\x85
const int UTF8NELLength = 2;
inline bool UTF8IsNEL(const unsigned char *us) noexcept {
return (us[0] == 0xc2) && (us[1] == 0x85);
}
enum { SURROGATE_LEAD_FIRST = 0xD800 };
enum { SURROGATE_LEAD_LAST = 0xDBFF };
enum { SURROGATE_TRAIL_FIRST = 0xDC00 };
enum { SURROGATE_TRAIL_LAST = 0xDFFF };
enum { SUPPLEMENTAL_PLANE_FIRST = 0x10000 };
inline constexpr unsigned int UTF16CharLength(wchar_t uch) noexcept {
return ((uch >= SURROGATE_LEAD_FIRST) && (uch <= SURROGATE_LEAD_LAST)) ? 2 : 1;
}
inline constexpr unsigned int UTF16LengthFromUTF8ByteCount(unsigned int byteCount) noexcept {
return (byteCount < 4) ? 1 : 2;
}
}
#endif

View File

@@ -0,0 +1,30 @@
// Scintilla source code edit control
/** @file UniqueString.h
** Define UniqueString, a unique_ptr based string type for storage in containers
** and an allocator for UniqueString.
**/
// Copyright 2017 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#ifndef UNIQUESTRING_H
#define UNIQUESTRING_H
namespace Scintilla {
using UniqueString = std::unique_ptr<const char[]>;
/// Equivalent to strdup but produces a std::unique_ptr<const char[]> allocation to go
/// into collections.
inline UniqueString UniqueStringCopy(const char *text) {
if (!text) {
return UniqueString();
}
const size_t len = strlen(text);
char *sNew = new char[len + 1];
std::copy(text, text + len + 1, sNew);
return UniqueString(sNew);
}
}
#endif

View File

@@ -0,0 +1,613 @@
// 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 <cstddef>
#include <cassert>
#include <cstring>
#include <stdexcept>
#include <vector>
#include <map>
#include <algorithm>
#include <memory>
#include "Platform.h"
#include "Scintilla.h"
#include "Position.h"
#include "UniqueString.h"
#include "Indicator.h"
#include "XPM.h"
#include "LineMarker.h"
#include "Style.h"
#include "ViewStyle.h"
using namespace Scintilla;
MarginStyle::MarginStyle(int style_, int width_, int mask_) :
style(style_), width(width_), mask(mask_), sensitive(false), cursor(SC_CURSORREVERSEARROW) {
}
// A list of the fontnames - avoids wasting space in each style
FontNames::FontNames() {
}
FontNames::~FontNames() {
Clear();
}
void FontNames::Clear() {
names.clear();
}
const char *FontNames::Save(const char *name) {
if (!name)
return nullptr;
for (const UniqueString &nm : names) {
if (strcmp(nm.get(), name) == 0) {
return nm.get();
}
}
names.push_back(UniqueStringCopy(name));
return names.back().get();
}
FontRealised::FontRealised() {
}
FontRealised::~FontRealised() {
font.Release();
}
void FontRealised::Realise(Surface &surface, int zoomLevel, int technology, const FontSpecification &fs) {
PLATFORM_ASSERT(fs.fontName);
sizeZoomed = fs.size + zoomLevel * SC_FONT_SIZE_MULTIPLIER;
if (sizeZoomed <= 2 * SC_FONT_SIZE_MULTIPLIER) // Hangs if sizeZoomed <= 1
sizeZoomed = 2 * SC_FONT_SIZE_MULTIPLIER;
const float deviceHeight = static_cast<float>(surface.DeviceHeightFont(sizeZoomed));
const FontParameters fp(fs.fontName, deviceHeight / SC_FONT_SIZE_MULTIPLIER, fs.weight, fs.italic, fs.extraFontFlag, technology, fs.characterSet);
font.Create(fp);
ascent = static_cast<unsigned int>(surface.Ascent(font));
descent = static_cast<unsigned int>(surface.Descent(font));
capitalHeight = surface.Ascent(font) - surface.InternalLeading(font);
aveCharWidth = surface.AverageCharWidth(font);
spaceWidth = surface.WidthText(font, " ", 1);
}
ViewStyle::ViewStyle() : markers(MARKER_MAX + 1), indicators(INDIC_MAX + 1) {
Init();
}
// Copy constructor only called when printing copies the screen ViewStyle so it can be
// modified for printing styles.
ViewStyle::ViewStyle(const ViewStyle &source) : markers(MARKER_MAX + 1), indicators(INDIC_MAX + 1) {
Init(source.styles.size());
styles = source.styles;
for (size_t sty=0; sty<source.styles.size(); sty++) {
// Can't just copy fontName as its lifetime is relative to its owning ViewStyle
styles[sty].fontName = fontNames.Save(source.styles[sty].fontName);
}
nextExtendedStyle = source.nextExtendedStyle;
markers = source.markers;
CalcLargestMarkerHeight();
indicators = source.indicators;
indicatorsDynamic = source.indicatorsDynamic;
indicatorsSetFore = source.indicatorsSetFore;
selColours = source.selColours;
selAdditionalForeground = source.selAdditionalForeground;
selAdditionalBackground = source.selAdditionalBackground;
selBackground2 = source.selBackground2;
selAlpha = source.selAlpha;
selAdditionalAlpha = source.selAdditionalAlpha;
selEOLFilled = source.selEOLFilled;
foldmarginColour = source.foldmarginColour;
foldmarginHighlightColour = source.foldmarginHighlightColour;
hotspotColours = source.hotspotColours;
hotspotUnderline = source.hotspotUnderline;
hotspotSingleLine = source.hotspotSingleLine;
whitespaceColours = source.whitespaceColours;
controlCharSymbol = source.controlCharSymbol;
controlCharWidth = source.controlCharWidth;
selbar = source.selbar;
selbarlight = source.selbarlight;
caretcolour = source.caretcolour;
additionalCaretColour = source.additionalCaretColour;
caretLineFrame = source.caretLineFrame;
showCaretLineBackground = source.showCaretLineBackground;
alwaysShowCaretLineBackground = source.alwaysShowCaretLineBackground;
caretLineBackground = source.caretLineBackground;
caretLineAlpha = source.caretLineAlpha;
caretStyle = source.caretStyle;
caretWidth = source.caretWidth;
someStylesProtected = false;
someStylesForceCase = false;
leftMarginWidth = source.leftMarginWidth;
rightMarginWidth = source.rightMarginWidth;
ms = source.ms;
maskInLine = source.maskInLine;
maskDrawInText = source.maskDrawInText;
fixedColumnWidth = source.fixedColumnWidth;
marginInside = source.marginInside;
textStart = source.textStart;
zoomLevel = source.zoomLevel;
viewWhitespace = source.viewWhitespace;
tabDrawMode = source.tabDrawMode;
whitespaceSize = source.whitespaceSize;
viewIndentationGuides = source.viewIndentationGuides;
viewEOL = source.viewEOL;
extraFontFlag = source.extraFontFlag;
extraAscent = source.extraAscent;
extraDescent = source.extraDescent;
marginStyleOffset = source.marginStyleOffset;
annotationVisible = source.annotationVisible;
annotationStyleOffset = source.annotationStyleOffset;
braceHighlightIndicatorSet = source.braceHighlightIndicatorSet;
braceHighlightIndicator = source.braceHighlightIndicator;
braceBadLightIndicatorSet = source.braceBadLightIndicatorSet;
braceBadLightIndicator = source.braceBadLightIndicator;
edgeState = source.edgeState;
theEdge = source.theEdge;
theMultiEdge = source.theMultiEdge;
marginNumberPadding = source.marginNumberPadding;
ctrlCharPadding = source.ctrlCharPadding;
lastSegItalicsOffset = source.lastSegItalicsOffset;
wrapState = source.wrapState;
wrapVisualFlags = source.wrapVisualFlags;
wrapVisualFlagsLocation = source.wrapVisualFlagsLocation;
wrapVisualStartIndent = source.wrapVisualStartIndent;
wrapIndentMode = source.wrapIndentMode;
}
ViewStyle::~ViewStyle() {
styles.clear();
fonts.clear();
}
void ViewStyle::CalculateMarginWidthAndMask() {
fixedColumnWidth = marginInside ? leftMarginWidth : 0;
maskInLine = 0xffffffff;
int maskDefinedMarkers = 0;
for (const MarginStyle &m : ms) {
fixedColumnWidth += m.width;
if (m.width > 0)
maskInLine &= ~m.mask;
maskDefinedMarkers |= m.mask;
}
maskDrawInText = 0;
for (int markBit = 0; markBit < 32; markBit++) {
const int maskBit = 1U << markBit;
switch (markers[markBit].markType) {
case SC_MARK_EMPTY:
maskInLine &= ~maskBit;
break;
case SC_MARK_BACKGROUND:
case SC_MARK_UNDERLINE:
maskInLine &= ~maskBit;
maskDrawInText |= maskDefinedMarkers & maskBit;
break;
}
}
}
void ViewStyle::Init(size_t stylesSize_) {
AllocStyles(stylesSize_);
nextExtendedStyle = 256;
fontNames.Clear();
ResetDefaultStyle();
// There are no image markers by default, so no need for calling CalcLargestMarkerHeight()
largestMarkerHeight = 0;
indicators[0] = Indicator(INDIC_SQUIGGLE, ColourDesired(0, 0x7f, 0));
indicators[1] = Indicator(INDIC_TT, ColourDesired(0, 0, 0xff));
indicators[2] = Indicator(INDIC_PLAIN, ColourDesired(0xff, 0, 0));
technology = SC_TECHNOLOGY_DEFAULT;
indicatorsDynamic = false;
indicatorsSetFore = false;
lineHeight = 1;
lineOverlap = 0;
maxAscent = 1;
maxDescent = 1;
aveCharWidth = 8;
spaceWidth = 8;
tabWidth = spaceWidth * 8;
selColours.fore = ColourOptional(ColourDesired(0xff, 0, 0));
selColours.back = ColourOptional(ColourDesired(0xc0, 0xc0, 0xc0), true);
selAdditionalForeground = ColourDesired(0xff, 0, 0);
selAdditionalBackground = ColourDesired(0xd7, 0xd7, 0xd7);
selBackground2 = ColourDesired(0xb0, 0xb0, 0xb0);
selAlpha = SC_ALPHA_NOALPHA;
selAdditionalAlpha = SC_ALPHA_NOALPHA;
selEOLFilled = false;
foldmarginColour = ColourOptional(ColourDesired(0xff, 0, 0));
foldmarginHighlightColour = ColourOptional(ColourDesired(0xc0, 0xc0, 0xc0));
whitespaceColours.fore = ColourOptional();
whitespaceColours.back = ColourOptional(ColourDesired(0xff, 0xff, 0xff));
controlCharSymbol = 0; /* Draw the control characters */
controlCharWidth = 0;
selbar = Platform::Chrome();
selbarlight = Platform::ChromeHighlight();
styles[STYLE_LINENUMBER].fore = ColourDesired(0, 0, 0);
styles[STYLE_LINENUMBER].back = Platform::Chrome();
caretcolour = ColourDesired(0, 0, 0);
additionalCaretColour = ColourDesired(0x7f, 0x7f, 0x7f);
caretLineFrame = 0;
showCaretLineBackground = false;
alwaysShowCaretLineBackground = false;
caretLineBackground = ColourDesired(0xff, 0xff, 0);
caretLineAlpha = SC_ALPHA_NOALPHA;
caretStyle = CARETSTYLE_LINE;
caretWidth = 1;
someStylesProtected = false;
someStylesForceCase = false;
hotspotColours.fore = ColourOptional(ColourDesired(0, 0, 0xff));
hotspotColours.back = ColourOptional(ColourDesired(0xff, 0xff, 0xff));
hotspotUnderline = true;
hotspotSingleLine = true;
leftMarginWidth = 1;
rightMarginWidth = 1;
ms.resize(SC_MAX_MARGIN + 1);
ms[0] = MarginStyle(SC_MARGIN_NUMBER);
ms[1] = MarginStyle(SC_MARGIN_SYMBOL, 16, ~SC_MASK_FOLDERS);
ms[2] = MarginStyle(SC_MARGIN_SYMBOL);
marginInside = true;
CalculateMarginWidthAndMask();
textStart = marginInside ? fixedColumnWidth : leftMarginWidth;
zoomLevel = 0;
viewWhitespace = wsInvisible;
tabDrawMode = tdLongArrow;
whitespaceSize = 1;
viewIndentationGuides = ivNone;
viewEOL = false;
extraFontFlag = 0;
extraAscent = 0;
extraDescent = 0;
marginStyleOffset = 0;
annotationVisible = ANNOTATION_HIDDEN;
annotationStyleOffset = 0;
braceHighlightIndicatorSet = false;
braceHighlightIndicator = 0;
braceBadLightIndicatorSet = false;
braceBadLightIndicator = 0;
edgeState = EDGE_NONE;
theEdge = EdgeProperties(0, ColourDesired(0xc0, 0xc0, 0xc0));
marginNumberPadding = 3;
ctrlCharPadding = 3; // +3 For a blank on front and rounded edge each side
lastSegItalicsOffset = 2;
wrapState = eWrapNone;
wrapVisualFlags = 0;
wrapVisualFlagsLocation = 0;
wrapVisualStartIndent = 0;
wrapIndentMode = SC_WRAPINDENT_FIXED;
}
void ViewStyle::Refresh(Surface &surface, int tabInChars) {
fonts.clear();
selbar = Platform::Chrome();
selbarlight = Platform::ChromeHighlight();
// Apply the extra font flag which controls text drawing quality to each style.
for (Style &style : styles) {
style.extraFontFlag = extraFontFlag;
}
// Create a FontRealised object for each unique font in the styles.
CreateAndAddFont(styles[STYLE_DEFAULT]);
for (const Style &style : styles) {
CreateAndAddFont(style);
}
// Ask platform to allocate each unique font.
for (std::pair<const FontSpecification, std::unique_ptr<FontRealised>> &font : fonts) {
font.second->Realise(surface, zoomLevel, technology, font.first);
}
// Set the platform font handle and measurements for each style.
for (Style &style : styles) {
FontRealised *fr = Find(style);
style.Copy(fr->font, *fr);
}
indicatorsDynamic = std::any_of(indicators.cbegin(), indicators.cend(),
[](const Indicator &indicator) { return indicator.IsDynamic(); });
indicatorsSetFore = std::any_of(indicators.cbegin(), indicators.cend(),
[](const Indicator &indicator) { return indicator.OverridesTextFore(); });
maxAscent = 1;
maxDescent = 1;
FindMaxAscentDescent();
maxAscent += extraAscent;
maxDescent += extraDescent;
lineHeight = maxAscent + maxDescent;
lineOverlap = lineHeight / 10;
if (lineOverlap < 2)
lineOverlap = 2;
if (lineOverlap > lineHeight)
lineOverlap = lineHeight;
someStylesProtected = std::any_of(styles.cbegin(), styles.cend(),
[](const Style &style) { return style.IsProtected(); });
someStylesForceCase = std::any_of(styles.cbegin(), styles.cend(),
[](const Style &style) { return style.caseForce != Style::caseMixed; });
aveCharWidth = styles[STYLE_DEFAULT].aveCharWidth;
spaceWidth = styles[STYLE_DEFAULT].spaceWidth;
tabWidth = spaceWidth * tabInChars;
controlCharWidth = 0.0;
if (controlCharSymbol >= 32) {
const char cc[2] = { static_cast<char>(controlCharSymbol), '\0' };
controlCharWidth = surface.WidthText(styles[STYLE_CONTROLCHAR].font, cc, 1);
}
CalculateMarginWidthAndMask();
textStart = marginInside ? fixedColumnWidth : leftMarginWidth;
}
void ViewStyle::ReleaseAllExtendedStyles() {
nextExtendedStyle = 256;
}
int ViewStyle::AllocateExtendedStyles(int numberStyles) {
const int startRange = nextExtendedStyle;
nextExtendedStyle += numberStyles;
EnsureStyle(nextExtendedStyle);
for (int i=startRange; i<nextExtendedStyle; i++) {
styles[i].ClearTo(styles[STYLE_DEFAULT]);
}
return startRange;
}
void ViewStyle::EnsureStyle(size_t index) {
if (index >= styles.size()) {
AllocStyles(index+1);
}
}
void ViewStyle::ResetDefaultStyle() {
styles[STYLE_DEFAULT].Clear(ColourDesired(0,0,0),
ColourDesired(0xff,0xff,0xff),
Platform::DefaultFontSize() * SC_FONT_SIZE_MULTIPLIER, fontNames.Save(Platform::DefaultFont()),
SC_CHARSET_DEFAULT,
SC_WEIGHT_NORMAL, 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<styles.size(); i++) {
if (i != STYLE_DEFAULT) {
styles[i].ClearTo(styles[STYLE_DEFAULT]);
}
}
styles[STYLE_LINENUMBER].back = Platform::Chrome();
// Set call tip fore/back to match the values previously set for call tips
styles[STYLE_CALLTIP].back = ColourDesired(0xff, 0xff, 0xff);
styles[STYLE_CALLTIP].fore = ColourDesired(0x80, 0x80, 0x80);
}
void ViewStyle::SetStyleFontName(int styleIndex, const char *name) {
styles[styleIndex].fontName = fontNames.Save(name);
}
bool ViewStyle::ProtectionActive() const {
return someStylesProtected;
}
int ViewStyle::ExternalMarginWidth() const {
return marginInside ? 0 : fixedColumnWidth;
}
int ViewStyle::MarginFromLocation(Point pt) const {
int margin = -1;
int x = marginInside ? 0 : -fixedColumnWidth;
for (size_t i = 0; i < ms.size(); i++) {
if ((pt.x >= x) && (pt.x < x + ms[i].width))
margin = static_cast<int>(i);
x += ms[i].width;
}
return margin;
}
bool ViewStyle::ValidStyle(size_t styleIndex) const {
return styleIndex < styles.size();
}
void ViewStyle::CalcLargestMarkerHeight() {
largestMarkerHeight = 0;
for (const LineMarker &marker : markers) {
switch (marker.markType) {
case SC_MARK_PIXMAP:
if (marker.pxpm && marker.pxpm->GetHeight() > largestMarkerHeight)
largestMarkerHeight = marker.pxpm->GetHeight();
break;
case SC_MARK_RGBAIMAGE:
if (marker.image && marker.image->GetHeight() > largestMarkerHeight)
largestMarkerHeight = marker.image->GetHeight();
break;
}
}
}
int ViewStyle::GetFrameWidth() const {
return Sci::clamp(caretLineFrame, 1, lineHeight / 3);
}
bool ViewStyle::IsLineFrameOpaque(bool caretActive, bool lineContainsCaret) const {
return caretLineFrame && (caretActive || alwaysShowCaretLineBackground) && showCaretLineBackground &&
(caretLineAlpha == SC_ALPHA_NOALPHA) && lineContainsCaret;
}
// 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.
ColourOptional ViewStyle::Background(int marksOfLine, bool caretActive, bool lineContainsCaret) const {
ColourOptional background;
if (!caretLineFrame && (caretActive || alwaysShowCaretLineBackground) && showCaretLineBackground &&
(caretLineAlpha == SC_ALPHA_NOALPHA) && lineContainsCaret) {
background = ColourOptional(caretLineBackground, true);
}
if (!background.isSet && marksOfLine) {
int marks = marksOfLine;
for (int markBit = 0; (markBit < 32) && marks; markBit++) {
if ((marks & 1) && (markers[markBit].markType == SC_MARK_BACKGROUND) &&
(markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
background = ColourOptional(markers[markBit].back, true);
}
marks >>= 1;
}
}
if (!background.isSet && maskInLine) {
int marksMasked = marksOfLine & maskInLine;
if (marksMasked) {
for (int markBit = 0; (markBit < 32) && marksMasked; markBit++) {
if ((marksMasked & 1) &&
(markers[markBit].alpha == SC_ALPHA_NOALPHA)) {
background = ColourOptional(markers[markBit].back, true);
}
marksMasked >>= 1;
}
}
}
return background;
}
bool ViewStyle::SelectionBackgroundDrawn() const {
return selColours.back.isSet &&
((selAlpha == SC_ALPHA_NOALPHA) || (selAdditionalAlpha == SC_ALPHA_NOALPHA));
}
bool ViewStyle::WhitespaceBackgroundDrawn() const {
return (viewWhitespace != wsInvisible) && (whitespaceColours.back.isSet);
}
bool ViewStyle::WhiteSpaceVisible(bool inIndent) const {
return (!inIndent && viewWhitespace == wsVisibleAfterIndent) ||
(inIndent && viewWhitespace == wsVisibleOnlyInIndent) ||
viewWhitespace == wsVisibleAlways;
}
ColourDesired ViewStyle::WrapColour() const {
if (whitespaceColours.fore.isSet)
return whitespaceColours.fore;
else
return styles[STYLE_DEFAULT].fore;
}
bool ViewStyle::SetWrapState(int wrapState_) {
WrapMode wrapStateWanted;
switch (wrapState_) {
case SC_WRAP_WORD:
wrapStateWanted = eWrapWord;
break;
case SC_WRAP_CHAR:
wrapStateWanted = eWrapChar;
break;
case SC_WRAP_WHITESPACE:
wrapStateWanted = eWrapWhitespace;
break;
default:
wrapStateWanted = eWrapNone;
break;
}
const bool changed = wrapState != wrapStateWanted;
wrapState = wrapStateWanted;
return changed;
}
bool ViewStyle::SetWrapVisualFlags(int wrapVisualFlags_) {
const bool changed = wrapVisualFlags != wrapVisualFlags_;
wrapVisualFlags = wrapVisualFlags_;
return changed;
}
bool ViewStyle::SetWrapVisualFlagsLocation(int wrapVisualFlagsLocation_) {
const bool changed = wrapVisualFlagsLocation != wrapVisualFlagsLocation_;
wrapVisualFlagsLocation = wrapVisualFlagsLocation_;
return changed;
}
bool ViewStyle::SetWrapVisualStartIndent(int wrapVisualStartIndent_) {
const bool changed = wrapVisualStartIndent != wrapVisualStartIndent_;
wrapVisualStartIndent = wrapVisualStartIndent_;
return changed;
}
bool ViewStyle::SetWrapIndentMode(int wrapIndentMode_) {
const bool changed = wrapIndentMode != wrapIndentMode_;
wrapIndentMode = wrapIndentMode_;
return changed;
}
void ViewStyle::AllocStyles(size_t sizeNew) {
size_t i=styles.size();
styles.resize(sizeNew);
if (styles.size() > STYLE_DEFAULT) {
for (; i<sizeNew; i++) {
if (i != STYLE_DEFAULT) {
styles[i].ClearTo(styles[STYLE_DEFAULT]);
}
}
}
}
void ViewStyle::CreateAndAddFont(const FontSpecification &fs) {
if (fs.fontName) {
FontMap::iterator it = fonts.find(fs);
if (it == fonts.end()) {
fonts[fs] = std::unique_ptr<FontRealised>(new FontRealised());
}
}
}
FontRealised *ViewStyle::Find(const FontSpecification &fs) {
if (!fs.fontName) // Invalid specification so return arbitrary object
return fonts.begin()->second.get();
FontMap::iterator it = fonts.find(fs);
if (it != fonts.end()) {
// Should always reach here since map was just set for all styles
return it->second.get();
}
return nullptr;
}
void ViewStyle::FindMaxAscentDescent() {
for (FontMap::const_iterator it = fonts.cbegin(); it != fonts.cend(); ++it) {
if (maxAscent < it->second->ascent)
maxAscent = it->second->ascent;
if (maxDescent < it->second->descent)
maxDescent = it->second->descent;
}
}

View File

@@ -0,0 +1,224 @@
// 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
namespace Scintilla {
/**
*/
class MarginStyle {
public:
int style;
ColourDesired back;
int width;
int mask;
bool sensitive;
int cursor;
MarginStyle(int style_= SC_MARGIN_SYMBOL, int width_=0, int mask_=0);
};
/**
*/
class FontNames {
private:
std::vector<UniqueString> names;
public:
FontNames();
// FontNames objects can not be copied.
FontNames(const FontNames &) = delete;
FontNames(FontNames &&) = delete;
FontNames &operator=(const FontNames &) = delete;
FontNames &operator=(FontNames &&) = delete;
~FontNames();
void Clear();
const char *Save(const char *name);
};
class FontRealised : public FontMeasurements {
public:
Font font;
FontRealised();
// FontRealised objects can not be copied.
FontRealised(const FontRealised &) = delete;
FontRealised(FontRealised &&) = delete;
FontRealised &operator=(const FontRealised &) = delete;
FontRealised &operator=(FontRealised &&) = delete;
virtual ~FontRealised();
void Realise(Surface &surface, int zoomLevel, int technology, const FontSpecification &fs);
};
enum IndentView {ivNone, ivReal, ivLookForward, ivLookBoth};
enum WhiteSpaceVisibility {wsInvisible=0, wsVisibleAlways=1, wsVisibleAfterIndent=2, wsVisibleOnlyInIndent=3};
enum TabDrawMode {tdLongArrow=0, tdStrikeOut=1};
typedef std::map<FontSpecification, std::unique_ptr<FontRealised>> FontMap;
enum WrapMode { eWrapNone, eWrapWord, eWrapChar, eWrapWhitespace };
class ColourOptional : public ColourDesired {
public:
bool isSet;
ColourOptional(ColourDesired colour_=ColourDesired(0,0,0), bool isSet_=false) : ColourDesired(colour_), isSet(isSet_) {
}
ColourOptional(uptr_t wParam, sptr_t lParam) : ColourDesired(static_cast<int>(lParam)), isSet(wParam != 0) {
}
};
struct ForeBackColours {
ColourOptional fore;
ColourOptional back;
};
struct EdgeProperties {
int column;
ColourDesired colour;
EdgeProperties(int column_ = 0, ColourDesired colour_ = ColourDesired(0)) :
column(column_), colour(colour_) {
}
EdgeProperties(uptr_t wParam, sptr_t lParam) :
column(static_cast<int>(wParam)), colour(static_cast<int>(lParam)) {
}
};
/**
*/
class ViewStyle {
FontNames fontNames;
FontMap fonts;
public:
std::vector<Style> styles;
int nextExtendedStyle;
std::vector<LineMarker> markers;
int largestMarkerHeight;
std::vector<Indicator> indicators;
bool indicatorsDynamic;
bool indicatorsSetFore;
int technology;
int lineHeight;
int lineOverlap;
unsigned int maxAscent;
unsigned int maxDescent;
XYPOSITION aveCharWidth;
XYPOSITION spaceWidth;
XYPOSITION tabWidth;
ForeBackColours selColours;
ColourDesired selAdditionalForeground;
ColourDesired selAdditionalBackground;
ColourDesired selBackground2;
int selAlpha;
int selAdditionalAlpha;
bool selEOLFilled;
ForeBackColours whitespaceColours;
int controlCharSymbol;
XYPOSITION controlCharWidth;
ColourDesired selbar;
ColourDesired selbarlight;
ColourOptional foldmarginColour;
ColourOptional foldmarginHighlightColour;
ForeBackColours hotspotColours;
bool hotspotUnderline;
bool hotspotSingleLine;
/// Margins are ordered: Line Numbers, Selection Margin, Spacing Margin
int leftMarginWidth; ///< Spacing margin on left of text
int rightMarginWidth; ///< Spacing margin on right of text
int maskInLine; ///< Mask for markers to be put into text because there is nowhere for them to go in margin
int maskDrawInText; ///< Mask for markers that always draw in text
std::vector<MarginStyle> ms;
int fixedColumnWidth; ///< Total width of margins
bool marginInside; ///< true: margin included in text view, false: separate views
int textStart; ///< Starting x position of text within the view
int zoomLevel;
WhiteSpaceVisibility viewWhitespace;
TabDrawMode tabDrawMode;
int whitespaceSize;
IndentView viewIndentationGuides;
bool viewEOL;
ColourDesired caretcolour;
ColourDesired additionalCaretColour;
int caretLineFrame;
bool showCaretLineBackground;
bool alwaysShowCaretLineBackground;
ColourDesired caretLineBackground;
int caretLineAlpha;
int caretStyle;
int caretWidth;
bool someStylesProtected;
bool someStylesForceCase;
int extraFontFlag;
int extraAscent;
int extraDescent;
int marginStyleOffset;
int annotationVisible;
int annotationStyleOffset;
bool braceHighlightIndicatorSet;
int braceHighlightIndicator;
bool braceBadLightIndicatorSet;
int braceBadLightIndicator;
int edgeState;
EdgeProperties theEdge;
std::vector<EdgeProperties> theMultiEdge;
int marginNumberPadding; // the right-side padding of the number margin
int ctrlCharPadding; // the padding around control character text blobs
int lastSegItalicsOffset; // the offset so as not to clip italic characters at EOLs
// Wrapping support
WrapMode wrapState;
int wrapVisualFlags;
int wrapVisualFlagsLocation;
int wrapVisualStartIndent;
int wrapIndentMode; // SC_WRAPINDENT_FIXED, _SAME, _INDENT
ViewStyle();
ViewStyle(const ViewStyle &source);
ViewStyle(ViewStyle &&) = delete;
// Can only be copied through copy constructor which ensures font names initialised correctly
ViewStyle &operator=(const ViewStyle &) = delete;
ViewStyle &operator=(ViewStyle &&) = delete;
~ViewStyle();
void CalculateMarginWidthAndMask();
void Init(size_t stylesSize_=256);
void Refresh(Surface &surface, int tabInChars);
void ReleaseAllExtendedStyles();
int AllocateExtendedStyles(int numberStyles);
void EnsureStyle(size_t index);
void ResetDefaultStyle();
void ClearStyles();
void SetStyleFontName(int styleIndex, const char *name);
bool ProtectionActive() const;
int ExternalMarginWidth() const;
int MarginFromLocation(Point pt) const;
bool ValidStyle(size_t styleIndex) const;
void CalcLargestMarkerHeight();
int GetFrameWidth() const;
bool IsLineFrameOpaque(bool caretActive, bool lineContainsCaret) const;
ColourOptional Background(int marksOfLine, bool caretActive, bool lineContainsCaret) const;
bool SelectionBackgroundDrawn() const;
bool WhitespaceBackgroundDrawn() const;
ColourDesired WrapColour() const;
bool SetWrapState(int wrapState_);
bool SetWrapVisualFlags(int wrapVisualFlags_);
bool SetWrapVisualFlagsLocation(int wrapVisualFlagsLocation_);
bool SetWrapVisualStartIndent(int wrapVisualStartIndent_);
bool SetWrapIndentMode(int wrapIndentMode_);
bool WhiteSpaceVisible(bool inIndent) const;
private:
void AllocStyles(size_t sizeNew);
void CreateAndAddFont(const FontSpecification &fs);
FontRealised *Find(const FontSpecification &fs);
void FindMaxAscentDescent();
};
}
#endif

View File

@@ -0,0 +1,448 @@
// 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 <cstdlib>
#include <cstring>
#include <stdexcept>
#include <vector>
#include <map>
#include <algorithm>
#include <iterator>
#include <memory>
#include "Platform.h"
#include "XPM.h"
using namespace Scintilla;
#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::Draw(Surface *surface, PRectangle &rc)
{
surface->DrawXPM(rc, this);
}
RGBAImage::RGBAImage(int width_, int height_, float scale_,
const unsigned char *pixels_)
: height(height_), width(width_), scale(scale_)
{
if (pixels_)
{
qim = new QImage(*reinterpret_cast<const QImage *>(pixels_));
}
else
{
#if QT_VERSION >= 0x040000
qim = new QImage(width_, height_, QImage::Format_ARGB32);
#else
qim = new QImage(width_, height_, 32);
qim->setAlphaBuffer(true);
#endif
qim->fill(0);
}
}
RGBAImage::RGBAImage(const XPM &xpm)
{
#if QT_VERSION >= 0x040000
qim = new QImage(xpm.Pixmap().toImage());
#else
qim = new QImage(xpm.Pixmap().convertToImage());
#endif
width = qim->width();
height = qim->height();
}
RGBAImage::~RGBAImage()
{
delete qim;
}
const unsigned char *RGBAImage::Pixels() const
{
return reinterpret_cast<const unsigned char *>(qim);
}
void RGBAImage::SetPixel(int x, int y, ColourDesired colour, int alpha)
{
QRgb rgba = qRgba(colour.GetRed(), colour.GetGreen(), colour.GetBlue(),
alpha);
uint index_or_rgb;
#if QT_VERSION >= 0x040000
switch (qim->format())
{
case QImage::Format_RGB32:
case QImage::Format_ARGB32:
index_or_rgb = rgba;
break;
case QImage::Format_ARGB32_Premultiplied:
{
uint a = alpha;
#if QT_POINTER_SIZE == 8
quint64 t = (((quint64(rgba)) | ((quint64(rgba)) << 24)) & 0x00ff00ff00ff00ff) * a;
t = (t + ((t >> 8) & 0xff00ff00ff00ff) + 0x80008000800080) >> 8;
t &= 0x000000ff00ff00ff;
index_or_rgb = (uint(t)) | (uint(t >> 24)) | (a << 24);
#else
uint t = (rgba & 0xff00ff) * a;
t = (t + ((t >> 8) & 0xff00ff) + 0x800080) >> 8;
t &= 0xff00ff;
rgba = ((rgba >> 8) & 0xff) * a;
rgba = (rgba + ((rgba >> 8) & 0xff) + 0x80);
rgba &= 0xff00;
index_or_rgb = rgba | t | (a << 24);
#endif
break;
}
default:
#if QT_VERSION >= 0x040600
index_or_rgb = qim->colorCount();
#else
index_or_rgb = qim->colorTable().count();
#endif
qim->setColor(index_or_rgb, rgba);
}
#else
if (qim->depth() == 32)
{
index_or_rgb = rgba;
}
else
{
index_or_rgb = qim->numColors();
qim->setNumColors(index_or_rgb + 1);
qim->setColor(index_or_rgb, rgba);
}
#endif
qim->setPixel(x, y, index_or_rgb);
}
#else
namespace {
const char *NextField(const char *s) {
// In case there are leading spaces in the string
while (*s == ' ') {
s++;
}
while (*s && *s != ' ') {
s++;
}
while (*s == ' ') {
s++;
}
return s;
}
// Data lines in XPM can be terminated either with NUL or "
size_t MeasureLength(const char *s) {
size_t i = 0;
while (s[i] && (s[i] != '\"'))
i++;
return i;
}
unsigned int ValueOfHex(const char ch) noexcept {
if (ch >= '0' && ch <= '9')
return ch - '0';
else if (ch >= 'A' && ch <= 'F')
return ch - 'A' + 10;
else if (ch >= 'a' && ch <= 'f')
return ch - 'a' + 10;
else
return 0;
}
ColourDesired ColourFromHex(const char *val) noexcept {
const unsigned int r = ValueOfHex(val[0]) * 16 + ValueOfHex(val[1]);
const unsigned int g = ValueOfHex(val[2]) * 16 + ValueOfHex(val[3]);
const unsigned int b = ValueOfHex(val[4]) * 16 + ValueOfHex(val[5]);
return ColourDesired(r, g, b);
}
}
ColourDesired XPM::ColourFromCode(int ch) const {
return colourCodeTable[ch];
}
void XPM::FillRun(Surface *surface, int code, int startX, int y, int x) const {
if ((code != codeTransparent) && (startX != x)) {
const PRectangle rc = PRectangle::FromInts(startX, y, x, y + 1);
surface->FillRectangle(rc, ColourFromCode(code));
}
}
XPM::XPM(const char *textForm) {
Init(textForm);
}
XPM::XPM(const char *const *linesForm) {
Init(linesForm);
}
XPM::~XPM() {
}
void XPM::Init(const char *textForm) {
// 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
std::vector<const char *> linesForm = LinesFormFromTextForm(textForm);
if (!linesForm.empty()) {
Init(&linesForm[0]);
}
} else {
// It is really in line form
Init(reinterpret_cast<const char * const *>(textForm));
}
}
void XPM::Init(const char *const *linesForm) {
height = 1;
width = 1;
nColours = 1;
pixels.clear();
codeTransparent = ' ';
if (!linesForm)
return;
std::fill(colourCodeTable, std::end(colourCodeTable), ColourDesired(0));
const char *line0 = linesForm[0];
width = atoi(line0);
line0 = NextField(line0);
height = atoi(line0);
pixels.resize(width*height);
line0 = NextField(line0);
nColours = atoi(line0);
line0 = NextField(line0);
if (atoi(line0) != 1) {
// Only one char per pixel is supported
return;
}
for (int c=0; c<nColours; c++) {
const char *colourDef = linesForm[c+1];
const char code = colourDef[0];
colourDef += 4;
ColourDesired colour(0xff, 0xff, 0xff);
if (*colourDef == '#') {
colour = ColourFromHex(colourDef+1);
} else {
codeTransparent = code;
}
colourCodeTable[static_cast<unsigned char>(code)] = colour;
}
for (int y=0; y<height; y++) {
const char *lform = linesForm[y+nColours+1];
const size_t len = MeasureLength(lform);
for (size_t x = 0; x<len; x++)
pixels[y * width + x] = lform[x];
}
}
void XPM::Draw(Surface *surface, const PRectangle &rc) {
if (pixels.empty()) {
return;
}
// Centre the pixmap
const int startY = static_cast<int>(rc.top + (rc.Height() - height) / 2);
const int startX = static_cast<int>(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++) {
const int code = pixels[y * width + 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);
}
}
void XPM::PixelAt(int x, int y, ColourDesired &colour, bool &transparent) const {
if (pixels.empty() || (x<0) || (x >= width) || (y<0) || (y >= height)) {
colour = ColourDesired(0);
transparent = true;
return;
}
const int code = pixels[y * width + x];
transparent = code == codeTransparent;
if (transparent) {
colour = ColourDesired(0);
} else {
colour = ColourFromCode(code);
}
}
std::vector<const char *> XPM::LinesFormFromTextForm(const char *textForm) {
// Build the lines form out of the text form
std::vector<const char *> linesForm;
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);
}
if (countQuotes / 2 >= strings) {
break; // Bad height or number of colors!
}
if ((countQuotes & 1) == 0) {
linesForm.push_back(textForm + j + 1);
}
countQuotes++;
}
}
if (textForm[j] == '\0' || countQuotes / 2 > strings) {
// Malformed XPM! Height + number of colors too high or too low
linesForm.clear();
}
return linesForm;
}
RGBAImage::RGBAImage(int width_, int height_, float scale_, const unsigned char *pixels_) :
height(height_), width(width_), scale(scale_) {
if (pixels_) {
pixelBytes.assign(pixels_, pixels_ + CountBytes());
} else {
pixelBytes.resize(CountBytes());
}
}
RGBAImage::RGBAImage(const XPM &xpm) {
height = xpm.GetHeight();
width = xpm.GetWidth();
scale = 1;
pixelBytes.resize(CountBytes());
for (int y=0; y<height; y++) {
for (int x=0; x<width; x++) {
ColourDesired colour;
bool transparent = false;
xpm.PixelAt(x, y, colour, transparent);
SetPixel(x, y, colour, transparent ? 0 : 255);
}
}
}
RGBAImage::~RGBAImage() {
}
int RGBAImage::CountBytes() const {
return width * height * 4;
}
const unsigned char *RGBAImage::Pixels() const {
return &pixelBytes[0];
}
void RGBAImage::SetPixel(int x, int y, ColourDesired colour, int alpha) {
unsigned char *pixel = &pixelBytes[0] + (y*width+x) * 4;
// RGBA
pixel[0] = colour.GetRed();
pixel[1] = colour.GetGreen();
pixel[2] = colour.GetBlue();
pixel[3] = static_cast<unsigned char>(alpha);
}
RGBAImageSet::RGBAImageSet() : height(-1), width(-1) {
}
RGBAImageSet::~RGBAImageSet() {
Clear();
}
/// Remove all images.
void RGBAImageSet::Clear() {
images.clear();
height = -1;
width = -1;
}
/// Add an image.
void RGBAImageSet::Add(int ident, RGBAImage *image) {
ImageMap::iterator it=images.find(ident);
if (it == images.end()) {
images[ident] = std::unique_ptr<RGBAImage>(image);
} else {
it->second.reset(image);
}
height = -1;
width = -1;
}
/// Get image by id.
RGBAImage *RGBAImageSet::Get(int ident) {
ImageMap::iterator it = images.find(ident);
if (it != images.end()) {
return it->second.get();
}
return nullptr;
}
/// Give the largest height of the set.
int RGBAImageSet::GetHeight() const {
if (height < 0) {
for (const std::pair<const int, std::unique_ptr<RGBAImage>> &image : images) {
if (height < image.second->GetHeight()) {
height = image.second->GetHeight();
}
}
}
return (height > 0) ? height : 0;
}
/// Give the largest width of the set.
int RGBAImageSet::GetWidth() const {
if (width < 0) {
for (const std::pair<const int, std::unique_ptr<RGBAImage>> &image : images) {
if (width < image.second->GetWidth()) {
width = image.second->GetWidth();
}
}
}
return (width > 0) ? width : 0;
}
#endif

View File

@@ -0,0 +1,130 @@
// Scintilla source code edit control
/** @file XPM.h
** Define a classes to hold image data in the X Pixmap (XPM) and RGBA formats.
**/
// 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 <qimage.h>
#include <qpixmap.h>
#endif
namespace Scintilla {
/**
* 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 Draw(Surface *surface, PRectangle &rc);
int GetHeight() const {return qpm.height();}
const QPixmap &Pixmap() const {return qpm;}
#else
int height=1;
int width=1;
int nColours=1;
std::vector<unsigned char> pixels;
ColourDesired colourCodeTable[256];
char codeTransparent=' ';
ColourDesired ColourFromCode(int ch) const;
void FillRun(Surface *surface, int code, int startX, int y, int x) const;
public:
explicit XPM(const char *textForm);
explicit XPM(const char *const *linesForm);
XPM(const XPM &) = delete;
XPM(XPM &&) = delete;
XPM &operator=(const XPM &) = delete;
XPM &operator=(XPM &&) = delete;
~XPM();
void Init(const char *textForm);
void Init(const char *const *linesForm);
/// Decompose image into runs and use FillRectangle for each run
void Draw(Surface *surface, const PRectangle &rc);
int GetHeight() const { return height; }
int GetWidth() const { return width; }
void PixelAt(int x, int y, ColourDesired &colour, bool &transparent) const;
private:
static std::vector<const char *>LinesFormFromTextForm(const char *textForm);
#endif
};
/**
* A translucent image stored as a sequence of RGBA bytes.
*/
class RGBAImage {
int height;
int width;
float scale;
#if defined(PLAT_QT)
QImage *qim;
#else
std::vector<unsigned char> pixelBytes;
#endif
public:
RGBAImage(int width_, int height_, float scale_, const unsigned char *pixels_);
explicit RGBAImage(const XPM &xpm);
// Deleted so RGBAImage objects can not be copied.
RGBAImage(const RGBAImage &) = delete;
RGBAImage(RGBAImage &&) = delete;
RGBAImage &operator=(const RGBAImage &) = delete;
RGBAImage &operator=(RGBAImage &&) = delete;
virtual ~RGBAImage();
int GetHeight() const { return height; }
int GetWidth() const { return width; }
float GetScale() const { return scale; }
float GetScaledHeight() const { return height / scale; }
float GetScaledWidth() const { return width / scale; }
#if !defined(PLAT_QT)
int CountBytes() const;
#endif
const unsigned char *Pixels() const;
void SetPixel(int x, int y, ColourDesired colour, int alpha);
};
#if !defined(PLAT_QT)
/**
* A collection of RGBAImage pixmaps indexed by integer id.
*/
class RGBAImageSet {
typedef std::map<int, std::unique_ptr<RGBAImage>> ImageMap;
ImageMap images;
mutable int height; ///< Memorize largest height of the set.
mutable int width; ///< Memorize largest width of the set.
public:
RGBAImageSet();
// Deleted so RGBAImageSet objects can not be copied.
RGBAImageSet(const RGBAImageSet &) = delete;
RGBAImageSet(RGBAImageSet &&) = delete;
RGBAImageSet &operator=(const RGBAImageSet &) = delete;
RGBAImageSet &operator=(RGBAImageSet &&) = delete;
~RGBAImageSet();
/// Remove all images.
void Clear();
/// Add an image.
void Add(int ident, RGBAImage *image);
/// Get image by id.
RGBAImage *Get(int ident);
/// Give the largest height of the set.
int GetHeight() const;
/// Give the largest width of the set.
int GetWidth() const;
};
#endif
}
#endif