[BUG_FIXED] (Author: François-R Boyer & Dave Brotherstone) Regex search and replace haning problem fix.
git-svn-id: svn://svn.tuxfamily.org/svnroot/notepadplus/repository/trunk@1009 f5eea248-9336-0410-98b8-ebc06183d4e3
This commit is contained in:
parent
27b2431ad3
commit
ffdebe56ed
@ -744,6 +744,7 @@ BOOL CALLBACK FindReplaceDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM lP
|
||||
{
|
||||
//Single actions
|
||||
case IDCANCEL:
|
||||
(*_ppEditView)->execute(SCI_CALLTIPCANCEL);
|
||||
display(false);
|
||||
break;
|
||||
case IDOK : // Find Next : only for FIND_DLG and REPLACE_DLG
|
||||
@ -1181,7 +1182,7 @@ BOOL CALLBACK FindReplaceDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM lP
|
||||
// true : the text2find is found
|
||||
// false : the text2find is not found
|
||||
|
||||
bool FindReplaceDlg::processFindNext(const TCHAR *txt2find, const FindOption *options, FindStatus *oFindStatus)
|
||||
bool FindReplaceDlg::processFindNext(const TCHAR *txt2find, const FindOption *options, FindStatus *oFindStatus, FindNextType findNextType /* = FINDNEXTTYPE_FINDNEXT */)
|
||||
{
|
||||
if (oFindStatus)
|
||||
*oFindStatus = FSFound;
|
||||
@ -1191,10 +1192,12 @@ bool FindReplaceDlg::processFindNext(const TCHAR *txt2find, const FindOption *op
|
||||
|
||||
const FindOption *pOptions = options?options:_env;
|
||||
|
||||
(*_ppEditView)->execute(SCI_CALLTIPCANCEL);
|
||||
|
||||
int stringSizeFind = lstrlen(txt2find);
|
||||
TCHAR *pText = new TCHAR[stringSizeFind + 1];
|
||||
lstrcpy(pText, txt2find);
|
||||
|
||||
|
||||
if (pOptions->_searchType == FindExtended) {
|
||||
stringSizeFind = Searching::convertExtendedToString(txt2find, pText, stringSizeFind);
|
||||
}
|
||||
@ -1202,14 +1205,16 @@ bool FindReplaceDlg::processFindNext(const TCHAR *txt2find, const FindOption *op
|
||||
int docLength = int((*_ppEditView)->execute(SCI_GETLENGTH));
|
||||
CharacterRange cr = (*_ppEditView)->getSelection();
|
||||
|
||||
|
||||
//The search "zone" is relative to the selection, so search happens 'outside'
|
||||
int startPosition = cr.cpMax;
|
||||
int endPosition = docLength;
|
||||
|
||||
|
||||
if (pOptions->_whichDirection == DIR_UP)
|
||||
{
|
||||
//When searching upwards, start is the lower part, end the upper, for backwards search
|
||||
startPosition = cr.cpMin - 1;
|
||||
startPosition = cr.cpMax - 1;
|
||||
endPosition = 0;
|
||||
}
|
||||
|
||||
@ -1222,21 +1227,47 @@ bool FindReplaceDlg::processFindNext(const TCHAR *txt2find, const FindOption *op
|
||||
else if (NextIncremental==pOptions->_incrementalType)
|
||||
{
|
||||
// text to find is not modified, so use current position +1
|
||||
startPosition = cr.cpMin +1;
|
||||
startPosition = cr.cpMin;
|
||||
endPosition = docLength;
|
||||
|
||||
if (pOptions->_whichDirection == DIR_UP)
|
||||
{
|
||||
//When searching upwards, start is the lower part, end the upper, for backwards search
|
||||
startPosition = cr.cpMax - 1;
|
||||
startPosition = cr.cpMax;
|
||||
endPosition = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int flags = Searching::buildSearchFlags(pOptions);
|
||||
switch (findNextType)
|
||||
{
|
||||
case FINDNEXTTYPE_FINDNEXT:
|
||||
flags |= SCFIND_REGEXP_EMPTYMATCH_ALL | SCFIND_REGEXP_SKIPCRLFASONE;
|
||||
break;
|
||||
|
||||
case FINDNEXTTYPE_REPLACENEXT:
|
||||
flags |= SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH | SCFIND_REGEXP_SKIPCRLFASONE;
|
||||
break;
|
||||
|
||||
case FINDNEXTTYPE_FINDNEXTFORREPLACE:
|
||||
flags |= SCFIND_REGEXP_EMPTYMATCH_ALL | SCFIND_REGEXP_EMPTYMATCH_ALLOWATSTART | SCFIND_REGEXP_SKIPCRLFASONE;
|
||||
break;
|
||||
}
|
||||
|
||||
int start, end;
|
||||
int posFind;
|
||||
|
||||
(*_ppEditView)->execute(SCI_SETSEARCHFLAGS, flags);
|
||||
|
||||
int posFind = (*_ppEditView)->searchInTarget(pText, stringSizeFind, startPosition, endPosition);
|
||||
|
||||
// Don't allow a search to start in the middle of a line end marker
|
||||
if ((*_ppEditView)->execute(SCI_GETCHARAT, startPosition - 1) == '\r'
|
||||
&& (*_ppEditView)->execute(SCI_GETCHARAT, startPosition) == '\n')
|
||||
{
|
||||
++startPosition;
|
||||
}
|
||||
|
||||
posFind = (*_ppEditView)->searchInTarget(pText, stringSizeFind, startPosition, endPosition);
|
||||
if (posFind == -1) //no match found in target, check if a new target should be used
|
||||
{
|
||||
if (pOptions->_isWrapAround)
|
||||
@ -1290,17 +1321,30 @@ bool FindReplaceDlg::processFindNext(const TCHAR *txt2find, const FindOption *op
|
||||
::MessageBox(_hParent, TEXT("Invalid regular expression"), TEXT("Find"), MB_ICONERROR | MB_OK);
|
||||
return false;
|
||||
}
|
||||
int start = posFind;
|
||||
int end = int((*_ppEditView)->execute(SCI_GETTARGETEND));
|
||||
|
||||
start = posFind;
|
||||
end = int((*_ppEditView)->execute(SCI_GETTARGETEND));
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// to make sure the found result is visible:
|
||||
// prevent recording of absolute positioning commands issued in the process
|
||||
(*_ppEditView)->execute(SCI_STOPRECORD);
|
||||
Searching::displaySectionCentered(start, end, *_ppEditView, pOptions->_whichDirection == DIR_DOWN);
|
||||
// Show a calltip for a zero length match
|
||||
if (start == end)
|
||||
{
|
||||
(*_ppEditView)->execute(SCI_CALLTIPSHOW, start, reinterpret_cast<LPARAM>("^ zero length match"));
|
||||
}
|
||||
if (::SendMessage(_hParent, WM_GETCURRENTMACROSTATUS,0,0) == MACRO_RECORDING_IN_PROGRESS)
|
||||
(*_ppEditView)->execute(SCI_STARTRECORD);
|
||||
|
||||
delete [] pText;
|
||||
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1310,60 +1354,62 @@ bool FindReplaceDlg::processFindNext(const TCHAR *txt2find, const FindOption *op
|
||||
// || the text is replaced, and do NOT find the next occurrence
|
||||
bool FindReplaceDlg::processReplace(const TCHAR *txt2find, const TCHAR *txt2replace, const FindOption *options)
|
||||
{
|
||||
bool moreMatches;
|
||||
|
||||
if (!txt2find || !txt2find[0] || !txt2replace)
|
||||
return false;
|
||||
|
||||
const FindOption *pOptions = options?options:_env;
|
||||
FindOption replaceOptions = options ? *options : *_env;
|
||||
replaceOptions._incrementalType = NextIncremental;
|
||||
|
||||
if ((*_ppEditView)->getCurrentBuffer()->isReadOnly()) return false;
|
||||
|
||||
int stringSizeFind = lstrlen(txt2find);
|
||||
int stringSizeReplace = lstrlen(txt2replace);
|
||||
TCHAR *pTextFind = new TCHAR[stringSizeFind + 1];
|
||||
TCHAR *pTextReplace = new TCHAR[stringSizeReplace + 1];
|
||||
lstrcpy(pTextFind, txt2find);
|
||||
lstrcpy(pTextReplace, txt2replace);
|
||||
|
||||
if (pOptions->_searchType == FindExtended) {
|
||||
stringSizeFind = Searching::convertExtendedToString(txt2find, pTextFind, stringSizeFind);
|
||||
stringSizeReplace = Searching::convertExtendedToString(txt2replace, pTextReplace, stringSizeReplace);
|
||||
}
|
||||
|
||||
bool isRegExp = pOptions->_searchType == FindRegex;
|
||||
int flags = Searching::buildSearchFlags(pOptions);
|
||||
CharacterRange cr = (*_ppEditView)->getSelection();
|
||||
|
||||
(*_ppEditView)->execute(SCI_SETSEARCHFLAGS, flags);
|
||||
int posFind = (*_ppEditView)->searchInTarget(pTextFind, stringSizeFind, cr.cpMin, cr.cpMax);
|
||||
if (posFind != -1)
|
||||
{
|
||||
if (isRegExp)
|
||||
{
|
||||
//For the rare re exp case. ex: replace ^ by AAA
|
||||
int start = int((*_ppEditView)->execute(SCI_GETTARGETSTART));
|
||||
|
||||
int replacedLen = (*_ppEditView)->replaceTargetRegExMode(pTextReplace);
|
||||
(*_ppEditView)->execute(SCI_SETSEL, start, start + replacedLen);
|
||||
}
|
||||
else
|
||||
Sci_CharacterRange currentSelection = (*_ppEditView)->getSelection();
|
||||
FindStatus status;
|
||||
moreMatches = processFindNext(txt2find, &replaceOptions, &status, FINDNEXTTYPE_FINDNEXTFORREPLACE);
|
||||
|
||||
if (moreMatches)
|
||||
{
|
||||
Sci_CharacterRange nextFind = (*_ppEditView)->getSelection();
|
||||
|
||||
// If the next find is the same as the last, then perform the replacement
|
||||
if (nextFind.cpMin == currentSelection.cpMin && nextFind.cpMax == currentSelection.cpMax)
|
||||
{
|
||||
int start = int((*_ppEditView)->execute(SCI_GETTARGETSTART));
|
||||
int replacedLen = (*_ppEditView)->replaceTarget(pTextReplace);
|
||||
(*_ppEditView)->execute(SCI_SETSEL, start, start + replacedLen);
|
||||
int stringSizeFind = lstrlen(txt2find);
|
||||
int stringSizeReplace = lstrlen(txt2replace);
|
||||
|
||||
TCHAR *pTextFind = new TCHAR[stringSizeFind + 1];
|
||||
TCHAR *pTextReplace = new TCHAR[stringSizeReplace + 1];
|
||||
lstrcpy(pTextFind, txt2find);
|
||||
lstrcpy(pTextReplace, txt2replace);
|
||||
|
||||
bool isRegExp = replaceOptions._searchType == FindRegex;
|
||||
|
||||
int start = currentSelection.cpMin;
|
||||
int replacedLen = 0;
|
||||
if (isRegExp)
|
||||
{
|
||||
replacedLen = (*_ppEditView)->replaceTargetRegExMode(pTextReplace);
|
||||
}
|
||||
else
|
||||
{
|
||||
replacedLen = (*_ppEditView)->replaceTarget(pTextReplace);
|
||||
}
|
||||
|
||||
|
||||
(*_ppEditView)->execute(SCI_SETSEL, start + replacedLen, start + replacedLen);
|
||||
|
||||
// Do the next find
|
||||
moreMatches = processFindNext(txt2find, &replaceOptions, &status, FINDNEXTTYPE_REPLACENEXT);
|
||||
}
|
||||
}
|
||||
else if (posFind == -2) // Invalid Regular expression
|
||||
{
|
||||
::MessageBox(_hParent, TEXT("Invalid regular expression"), TEXT("Find"), MB_ICONERROR | MB_OK);
|
||||
return false;
|
||||
}
|
||||
|
||||
delete [] pTextFind;
|
||||
delete [] pTextReplace;
|
||||
return processFindNext(txt2find, pOptions); //after replacing, find the next section for selection
|
||||
return moreMatches;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int FindReplaceDlg::markAll(const TCHAR *txt2find, int styleID)
|
||||
{
|
||||
FindOption opt;
|
||||
@ -1517,9 +1563,14 @@ int FindReplaceDlg::processRange(ProcessOperation op, const TCHAR *txt2find, con
|
||||
}
|
||||
|
||||
bool isRegExp = pOptions->_searchType == FindRegex;
|
||||
int flags = Searching::buildSearchFlags(pOptions);
|
||||
|
||||
int flags = Searching::buildSearchFlags(pOptions) | SCFIND_REGEXP_SKIPCRLFASONE;
|
||||
|
||||
// Allow empty matches, but not immediately after previous match for replace all or find all.
|
||||
// Other search types should ignore empty matches completely.
|
||||
if (op == ProcessReplaceAll || op == ProcessFindAll)
|
||||
flags |= SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH;
|
||||
|
||||
|
||||
|
||||
if (op == ProcessMarkAll && colourStyleID == -1) //if marking, check if purging is needed
|
||||
{
|
||||
@ -1535,37 +1586,38 @@ int FindReplaceDlg::processRange(ProcessOperation op, const TCHAR *txt2find, con
|
||||
|
||||
//Initial range for searching
|
||||
(*_ppEditView)->execute(SCI_SETSEARCHFLAGS, flags);
|
||||
targetStart = (*_ppEditView)->searchInTarget(pTextFind, stringSizeFind, startRange, endRange);
|
||||
|
||||
if ((targetStart >= 0) && (op == ProcessFindAll)) //add new filetitle if this file results in hits
|
||||
{
|
||||
_pFinder->addFileNameTitle(fileName);
|
||||
}
|
||||
|
||||
bool findAllFileNameAdded = false;
|
||||
|
||||
while (targetStart != -1 && targetStart != -2)
|
||||
{
|
||||
//int posFindBefore = posFind;
|
||||
targetStart = int((*_ppEditView)->execute(SCI_GETTARGETSTART));
|
||||
targetStart = (*_ppEditView)->searchInTarget(pTextFind, stringSizeFind, startRange, endRange);
|
||||
|
||||
// If we've not found anything, just break out of the loop
|
||||
if (targetStart == -1 || targetStart == -2)
|
||||
break;
|
||||
|
||||
targetEnd = int((*_ppEditView)->execute(SCI_GETTARGETEND));
|
||||
|
||||
if (targetEnd > endRange) { //we found a result but outside our range, therefore do not process it
|
||||
break;
|
||||
}
|
||||
|
||||
int foundTextLen = targetEnd - targetStart;
|
||||
int replaceDelta = 0;
|
||||
|
||||
// Search resulted in empty token, possible with RE
|
||||
/*
|
||||
if (!foundTextLen) {
|
||||
delete [] pTextFind;
|
||||
delete [] pTextReplace;
|
||||
return -1;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case ProcessFindAll:
|
||||
{
|
||||
if (!findAllFileNameAdded) //add new filetitle in hits if we haven't already
|
||||
{
|
||||
_pFinder->addFileNameTitle(fileName);
|
||||
findAllFileNameAdded = true;
|
||||
}
|
||||
|
||||
int lineNumber = (*_ppEditView)->execute(SCI_LINEFROMPOSITION, targetStart);
|
||||
int lend = (*_ppEditView)->execute(SCI_GETLINEENDPOSITION, lineNumber);
|
||||
int lstart = (*_ppEditView)->execute(SCI_POSITIONFROMLINE, lineNumber);
|
||||
@ -1619,8 +1671,15 @@ int FindReplaceDlg::processRange(ProcessOperation op, const TCHAR *txt2find, con
|
||||
|
||||
case ProcessMarkAll:
|
||||
{
|
||||
(*_ppEditView)->execute(SCI_SETINDICATORCURRENT, SCE_UNIVERSAL_FOUND_STYLE);
|
||||
(*_ppEditView)->execute(SCI_INDICATORFILLRANGE, targetStart, foundTextLen);
|
||||
// In theory, we can't have empty matches for a ProcessMarkAll, but because scintilla
|
||||
// gets upset if we call INDICATORFILLRANGE with a length of 0, we protect against it here.
|
||||
// At least in version 2.27, after calling INDICATORFILLRANGE with length 0, further indicators
|
||||
// on the same line would simply not be shown. This may have been fixed in later version of Scintilla.
|
||||
if (foundTextLen > 0)
|
||||
{
|
||||
(*_ppEditView)->execute(SCI_SETINDICATORCURRENT, SCE_UNIVERSAL_FOUND_STYLE);
|
||||
(*_ppEditView)->execute(SCI_INDICATORFILLRANGE, targetStart, foundTextLen);
|
||||
}
|
||||
|
||||
if (_env->_doMarkLine)
|
||||
{
|
||||
@ -1635,22 +1694,34 @@ int FindReplaceDlg::processRange(ProcessOperation op, const TCHAR *txt2find, con
|
||||
|
||||
case ProcessMarkAllExt:
|
||||
{
|
||||
(*_ppEditView)->execute(SCI_SETINDICATORCURRENT, colourStyleID);
|
||||
(*_ppEditView)->execute(SCI_INDICATORFILLRANGE, targetStart, foundTextLen);
|
||||
// See comment by ProcessMarkAll
|
||||
if (foundTextLen > 0)
|
||||
{
|
||||
(*_ppEditView)->execute(SCI_SETINDICATORCURRENT, colourStyleID);
|
||||
(*_ppEditView)->execute(SCI_INDICATORFILLRANGE, targetStart, foundTextLen);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ProcessMarkAll_2:
|
||||
{
|
||||
(*_ppEditView)->execute(SCI_SETINDICATORCURRENT, SCE_UNIVERSAL_FOUND_STYLE_SMART);
|
||||
(*_ppEditView)->execute(SCI_INDICATORFILLRANGE, targetStart, foundTextLen);
|
||||
// See comment by ProcessMarkAll
|
||||
if (foundTextLen > 0)
|
||||
{
|
||||
(*_ppEditView)->execute(SCI_SETINDICATORCURRENT, SCE_UNIVERSAL_FOUND_STYLE_SMART);
|
||||
(*_ppEditView)->execute(SCI_INDICATORFILLRANGE, targetStart, foundTextLen);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ProcessMarkAll_IncSearch:
|
||||
{
|
||||
(*_ppEditView)->execute(SCI_SETINDICATORCURRENT, SCE_UNIVERSAL_FOUND_STYLE_INC);
|
||||
(*_ppEditView)->execute(SCI_INDICATORFILLRANGE, targetStart, foundTextLen);
|
||||
// See comment by ProcessMarkAll
|
||||
if (foundTextLen > 0)
|
||||
{
|
||||
(*_ppEditView)->execute(SCI_SETINDICATORCURRENT, SCE_UNIVERSAL_FOUND_STYLE_INC);
|
||||
(*_ppEditView)->execute(SCI_INDICATORFILLRANGE, targetStart, foundTextLen);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1675,14 +1746,14 @@ int FindReplaceDlg::processRange(ProcessOperation op, const TCHAR *txt2find, con
|
||||
if( targetStart + foundTextLen == endRange )
|
||||
break;
|
||||
|
||||
if (!foundTextLen && !replaceDelta)
|
||||
startRange = targetStart + foundTextLen + 1; // find a empty string so just step forword
|
||||
else
|
||||
startRange = targetStart + foundTextLen + replaceDelta; //search from result onwards
|
||||
|
||||
|
||||
startRange = targetStart + foundTextLen + replaceDelta; //search from result onwards
|
||||
endRange += replaceDelta; //adjust end of range in case of replace
|
||||
|
||||
targetStart = (*_ppEditView)->searchInTarget(pTextFind, stringSizeFind, startRange, endRange);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
delete [] pTextFind;
|
||||
delete [] pTextReplace;
|
||||
|
||||
|
@ -180,8 +180,17 @@ private:
|
||||
static SearchResultMarking EmptySearchResultMarking;
|
||||
};
|
||||
|
||||
|
||||
enum FindStatus { FSFound, FSNotFound, FSTopReached, FSEndReached};
|
||||
|
||||
|
||||
enum FindNextType {
|
||||
FINDNEXTTYPE_FINDNEXT,
|
||||
FINDNEXTTYPE_REPLACENEXT,
|
||||
FINDNEXTTYPE_FINDNEXTFORREPLACE
|
||||
};
|
||||
|
||||
|
||||
class FindReplaceDlg : public StaticDialog
|
||||
{
|
||||
friend class FindIncrementDlg;
|
||||
@ -208,7 +217,7 @@ public :
|
||||
void initOptionsFromDlg();
|
||||
|
||||
void doDialog(DIALOG_TYPE whichType, bool isRTL = false, bool toShow = true);
|
||||
bool processFindNext(const TCHAR *txt2find, const FindOption *options = NULL, FindStatus *oFindStatus = NULL);
|
||||
bool processFindNext(const TCHAR *txt2find, const FindOption *options = NULL, FindStatus *oFindStatus = NULL, FindNextType findNextType = FINDNEXTTYPE_FINDNEXT);
|
||||
bool processReplace(const TCHAR *txt2find, const TCHAR *txt2replace, const FindOption *options = NULL);
|
||||
|
||||
int markAll(const TCHAR *txt2find, int styleID);
|
||||
@ -317,6 +326,8 @@ private :
|
||||
TabBar _tab;
|
||||
winVer _winVer;
|
||||
|
||||
|
||||
|
||||
void enableReplaceFunc(bool isEnable);
|
||||
void enableFindInFilesControls(bool isEnable = true);
|
||||
void enableFindInFilesFunc();
|
||||
@ -347,6 +358,8 @@ private :
|
||||
static const int FR_OP_FIF = 4;
|
||||
static const int FR_OP_GLOBAL = 8;
|
||||
void saveInMacro(int cmd, int cmdType);
|
||||
|
||||
|
||||
};
|
||||
|
||||
//FindIncrementDlg: incremental search dialog, docked in rebar
|
||||
@ -402,4 +415,7 @@ private :
|
||||
void markSelectedTextInc(bool enable, FindOption *opt = NULL);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif //FIND_REPLACE_DLG_H
|
||||
|
@ -4,7 +4,7 @@
|
||||
* Converted from boost::xpressive to boost::regex and performance improvements
|
||||
* (principally caching the compiled regex), and support for UTF8 encoded text
|
||||
* (c) 2012 Dave Brotherstone - Changes for boost::regex
|
||||
*
|
||||
* (c) 2013 Francois-R.Boyer@PolyMtl.ca - Empty match modes and best match backward search.
|
||||
*
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
@ -35,49 +35,196 @@ using namespace Scintilla;
|
||||
|
||||
using namespace boost;
|
||||
|
||||
|
||||
typedef basic_regex<char> charregex_t;
|
||||
typedef boost::wregex wcharregex_t;
|
||||
// , std::vector<boost::sub_match<DocumentIterator> >::allocator_type
|
||||
typedef match_results<UTF8DocumentIterator> utf8match_t;
|
||||
typedef match_results<AnsiDocumentIterator> ansimatch_t;
|
||||
|
||||
class BoostRegexSearch : public RegexSearchBase
|
||||
{
|
||||
public:
|
||||
BoostRegexSearch() : substituted(NULL), lastCompileFlags(-1) {}
|
||||
BoostRegexSearch() : _substituted(NULL) {}
|
||||
|
||||
virtual ~BoostRegexSearch()
|
||||
{
|
||||
if (substituted)
|
||||
{
|
||||
delete [] substituted;
|
||||
substituted = NULL;
|
||||
}
|
||||
delete[] _substituted;
|
||||
_substituted = NULL;
|
||||
}
|
||||
|
||||
|
||||
virtual long FindText(Document* doc, int minPos, int maxPos, const char *s,
|
||||
bool caseSensitive, bool word, bool wordStart, int flags, int *length);
|
||||
|
||||
|
||||
virtual long FindText(Document* doc, int startPosition, int endPosition, const char *regex,
|
||||
bool caseSensitive, bool word, bool wordStart, int sciSearchFlags, int *lengthRet);
|
||||
|
||||
virtual const char *SubstituteByPosition(Document* doc, const char *text, int *length);
|
||||
|
||||
private:
|
||||
wchar_t *utf8ToWchar(const char *utf8);
|
||||
char *wcharToUtf8(const wchar_t *w);
|
||||
class SearchParameters;
|
||||
|
||||
charregex_t m_charre;
|
||||
wcharregex_t m_wcharre;
|
||||
class Match : private DocWatcher {
|
||||
public:
|
||||
Match() : _document(NULL), _documentModified(false), _position(-1), _endPosition(-1), _endPositionForContinuationCheck(-1) {}
|
||||
~Match() { setDocument(NULL); }
|
||||
Match(Document* document, int position = -1, int endPosition = -1) : _document(NULL) { set(document, position, endPosition); }
|
||||
Match& operator=(Match& m) {
|
||||
set(m._document, m.position(), m.endPosition());
|
||||
return *this;
|
||||
}
|
||||
Match& operator=(int /*nullptr*/) {
|
||||
_position = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void set(Document* document = NULL, int position = -1, int endPosition = -1) {
|
||||
setDocument(document);
|
||||
_position = position;
|
||||
_endPositionForContinuationCheck = _endPosition = endPosition;
|
||||
_documentModified = false;
|
||||
}
|
||||
|
||||
bool isContinuationSearch(Document* document, int startPosition, int direction) {
|
||||
if (hasDocumentChanged(document))
|
||||
return false;
|
||||
if (direction > 0)
|
||||
return startPosition == _endPositionForContinuationCheck;
|
||||
else
|
||||
return startPosition == _position;
|
||||
}
|
||||
bool isEmpty() {
|
||||
return _position == _endPosition;
|
||||
}
|
||||
int position() {
|
||||
return _position;
|
||||
}
|
||||
int endPosition() {
|
||||
return _endPosition;
|
||||
}
|
||||
int length() {
|
||||
return _endPosition - _position;
|
||||
}
|
||||
int found() {
|
||||
return _position >= 0;
|
||||
}
|
||||
|
||||
private:
|
||||
bool hasDocumentChanged(Document* currentDocument) {
|
||||
return currentDocument != _document || _documentModified;
|
||||
}
|
||||
void setDocument(Document* newDocument) {
|
||||
if (newDocument != _document)
|
||||
{
|
||||
if (_document != NULL)
|
||||
_document->RemoveWatcher(this, NULL);
|
||||
_document = newDocument;
|
||||
if (_document != NULL)
|
||||
_document->AddWatcher(this, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
// DocWatcher, so we can track modifications to know if we should consider a search to be a continuation of last search:
|
||||
virtual void NotifyModified(Document* modifiedDocument, DocModification mh, void* /*userData*/)
|
||||
{
|
||||
if (modifiedDocument == _document)
|
||||
{
|
||||
if (mh.modificationType & (SC_PERFORMED_UNDO | SC_PERFORMED_REDO))
|
||||
_documentModified = true;
|
||||
// Replacing last found text should not make isContinuationSearch return false.
|
||||
else if (mh.modificationType & SC_MOD_DELETETEXT)
|
||||
{
|
||||
if (mh.position == position() && mh.length == length()) // Deleting what we last found.
|
||||
_endPositionForContinuationCheck = _position;
|
||||
else _documentModified = true;
|
||||
}
|
||||
else if (mh.modificationType & SC_MOD_INSERTTEXT)
|
||||
{
|
||||
if (mh.position == position() && position() == _endPositionForContinuationCheck) // Replace at last found position.
|
||||
_endPositionForContinuationCheck += mh.length;
|
||||
else _documentModified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
virtual void NotifyDeleted(Document* deletedDocument, void* /*userData*/)
|
||||
{
|
||||
if (deletedDocument == _document)
|
||||
set(NULL);
|
||||
}
|
||||
virtual void NotifyModifyAttempt(Document* /*document*/, void* /*userData*/) {}
|
||||
virtual void NotifySavePoint(Document* /*document*/, void* /*userData*/, bool /*atSavePoint*/) {}
|
||||
virtual void NotifyStyleNeeded(Document* /*document*/, void* /*userData*/, int /*endPos*/) {}
|
||||
virtual void NotifyLexerChanged(Document* /*document*/, void* /*userData*/) {}
|
||||
virtual void NotifyErrorOccurred(Document* /*document*/, void* /*userData*/, int /*status*/) {}
|
||||
|
||||
Document* _document;
|
||||
bool _documentModified;
|
||||
int _position, _endPosition;
|
||||
int _endPositionForContinuationCheck;
|
||||
};
|
||||
|
||||
utf8match_t m_utf8match;
|
||||
ansimatch_t m_ansimatch;
|
||||
class CharTPtr { // Automatically translatable from utf8 to wchar_t*, if required, with allocation and deallocation on destruction; char* is not deallocated.
|
||||
public:
|
||||
CharTPtr(const char* ptr) : _charPtr(ptr), _wcharPtr(NULL) {}
|
||||
~CharTPtr() {
|
||||
delete[] _wcharPtr;
|
||||
}
|
||||
operator const char*() {
|
||||
return _charPtr;
|
||||
}
|
||||
operator const wchar_t*() {
|
||||
if (_wcharPtr == NULL)
|
||||
_wcharPtr = utf8ToWchar(_charPtr);
|
||||
return _wcharPtr;
|
||||
}
|
||||
private:
|
||||
const char* _charPtr;
|
||||
wchar_t* _wcharPtr;
|
||||
};
|
||||
|
||||
template <class CharT, class CharacterIterator>
|
||||
class EncodingDependent {
|
||||
public:
|
||||
EncodingDependent() : _lastCompileFlags(-1) {}
|
||||
void compileRegex(const char *regex, const int compileFlags);
|
||||
Match FindText(SearchParameters& search);
|
||||
char *SubstituteByPosition(const char *text, int *length);
|
||||
private:
|
||||
Match FindTextForward(SearchParameters& search);
|
||||
Match FindTextBackward(SearchParameters& search);
|
||||
|
||||
public:
|
||||
typedef CharT Char;
|
||||
typedef basic_regex<CharT> Regex;
|
||||
typedef match_results<CharacterIterator> MatchResults;
|
||||
|
||||
MatchResults _match;
|
||||
private:
|
||||
Regex _regex;
|
||||
std::string _lastRegexString;
|
||||
int _lastCompileFlags;
|
||||
};
|
||||
|
||||
char *substituted;
|
||||
std::string m_lastRegexString;
|
||||
std::string m_lastRegexUtf8string;
|
||||
int lastCompileFlags;
|
||||
class SearchParameters {
|
||||
public:
|
||||
int nextCharacter(int position);
|
||||
bool isLineStart(int position);
|
||||
bool isLineEnd(int position);
|
||||
|
||||
Document* _document;
|
||||
const char *_regexString;
|
||||
int _compileFlags;
|
||||
int _startPosition;
|
||||
int _endPosition;
|
||||
regex_constants::match_flag_type _boostRegexFlags;
|
||||
int _direction;
|
||||
bool _is_allowed_empty;
|
||||
bool _is_allowed_empty_at_start_position;
|
||||
bool _skip_windows_line_end_as_one_character;
|
||||
};
|
||||
|
||||
static wchar_t *utf8ToWchar(const char *utf8);
|
||||
static char *wcharToUtf8(const wchar_t *w);
|
||||
static char *stringToCharPtr(const std::string& str);
|
||||
static char *stringToCharPtr(const std::wstring& str);
|
||||
|
||||
EncodingDependent<char, AnsiDocumentIterator> _ansi;
|
||||
EncodingDependent<wchar_t, UTF8DocumentIterator> _utf8;
|
||||
|
||||
char *_substituted;
|
||||
|
||||
Match _lastMatch;
|
||||
int _lastDirection;
|
||||
};
|
||||
|
||||
#ifdef SCI_NAMESPACE
|
||||
@ -96,200 +243,199 @@ RegexSearchBase *CreateRegexSearch(CharClassify* /* charClassTable */)
|
||||
|
||||
/**
|
||||
* Find text in document, supporting both forward and backward
|
||||
* searches (just pass minPos > maxPos to do a backward search)
|
||||
* searches (just pass startPosition > endPosition to do a backward search).
|
||||
*/
|
||||
|
||||
|
||||
long BoostRegexSearch::FindText(Document* doc, int minPos, int maxPos, const char *s,
|
||||
bool caseSensitive, bool /*word*/, bool /*wordStart*/, int searchFlags, int *length)
|
||||
long BoostRegexSearch::FindText(Document* doc, int startPosition, int endPosition, const char *regexString,
|
||||
bool caseSensitive, bool /*word*/, bool /*wordStart*/, int sciSearchFlags, int *lengthRet)
|
||||
{
|
||||
int startPos, endPos, increment;
|
||||
|
||||
if (minPos > maxPos)
|
||||
{
|
||||
startPos = maxPos;
|
||||
endPos = minPos;
|
||||
increment = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
startPos = minPos;
|
||||
endPos = maxPos;
|
||||
increment = 1;
|
||||
}
|
||||
|
||||
// Range endpoints should not be inside DBCS characters, but just in case, move them.
|
||||
startPos = doc->MovePositionOutsideChar(startPos, 1, false);
|
||||
endPos = doc->MovePositionOutsideChar(endPos, 1, false);
|
||||
|
||||
|
||||
int compileFlags(regex_constants::ECMAScript);
|
||||
if (!caseSensitive)
|
||||
{
|
||||
compileFlags |= regex_constants::icase;
|
||||
}
|
||||
bool isUtf8 = (doc->CodePage() == SC_CP_UTF8);
|
||||
|
||||
try
|
||||
{
|
||||
try {
|
||||
SearchParameters search;
|
||||
|
||||
if (compileFlags != lastCompileFlags
|
||||
|| (isUtf8 && m_lastRegexUtf8string != s)
|
||||
|| (!isUtf8 && m_lastRegexString != s)) // Test to see if we're called with the same
|
||||
// regex as last time, if we are, then we don't need to recompile it
|
||||
search._document = doc;
|
||||
|
||||
if (startPosition > endPosition
|
||||
|| startPosition == endPosition && _lastDirection < 0) // If we search in an empty region, suppose the direction is the same as last search (this is only important to verify if there can be an empty match in that empty region).
|
||||
{
|
||||
if (isUtf8)
|
||||
{
|
||||
const wchar_t* wchars = utf8ToWchar(s);
|
||||
m_wcharre = wcharregex_t(wchars, static_cast<regex_constants::syntax_option_type>(compileFlags));
|
||||
delete [] wchars;
|
||||
m_lastRegexUtf8string = s;
|
||||
}
|
||||
else
|
||||
{ // Ansi
|
||||
m_charre = charregex_t(s, static_cast<regex_constants::syntax_option_type>(compileFlags));
|
||||
m_lastRegexString = s;
|
||||
}
|
||||
lastCompileFlags = compileFlags;
|
||||
search._startPosition = endPosition;
|
||||
search._endPosition = startPosition;
|
||||
search._direction = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
search._startPosition = startPosition;
|
||||
search._endPosition = endPosition;
|
||||
search._direction = 1;
|
||||
}
|
||||
_lastDirection = search._direction;
|
||||
|
||||
// Range endpoints should not be inside DBCS characters, but just in case, move them.
|
||||
search._startPosition = doc->MovePositionOutsideChar(search._startPosition, 1, false);
|
||||
search._endPosition = doc->MovePositionOutsideChar(search._endPosition, 1, false);
|
||||
|
||||
const bool isUtf8 = (doc->CodePage() == SC_CP_UTF8);
|
||||
search._compileFlags =
|
||||
regex_constants::ECMAScript
|
||||
| (caseSensitive ? 0 : regex_constants::icase);
|
||||
search._regexString = regexString;
|
||||
|
||||
const bool starts_at_line_start = search.isLineStart(search._startPosition);
|
||||
const bool ends_at_line_end = search.isLineEnd(search._endPosition);
|
||||
search._boostRegexFlags =
|
||||
(starts_at_line_start ? regex_constants::match_default : regex_constants::match_not_bol)
|
||||
| (ends_at_line_end ? regex_constants::match_default : regex_constants::match_not_eol)
|
||||
| ((sciSearchFlags & SCFIND_REGEXP_DOTMATCHESNL) ? regex_constants::match_default : regex_constants::match_not_dot_newline);
|
||||
|
||||
const int empty_match_style = sciSearchFlags & SCFIND_REGEXP_EMPTYMATCH_MASK;
|
||||
const int allow_empty_at_start = sciSearchFlags & SCFIND_REGEXP_EMPTYMATCH_ALLOWATSTART;
|
||||
|
||||
search._is_allowed_empty = empty_match_style != SCFIND_REGEXP_EMPTYMATCH_NONE;
|
||||
search._is_allowed_empty_at_start_position = search._is_allowed_empty &&
|
||||
(allow_empty_at_start
|
||||
|| !_lastMatch.isContinuationSearch(doc, startPosition, search._direction)
|
||||
|| empty_match_style == SCFIND_REGEXP_EMPTYMATCH_ALL && !_lastMatch.isEmpty() // If last match is empty and this is a continuation, then we would have same empty match at start position, if it was allowed.
|
||||
);
|
||||
search._skip_windows_line_end_as_one_character = (sciSearchFlags & SCFIND_REGEXP_SKIPCRLFASONE) != 0;
|
||||
|
||||
Match match =
|
||||
isUtf8 ? _utf8.FindText(search)
|
||||
: _ansi.FindText(search);
|
||||
|
||||
if (match.found())
|
||||
{
|
||||
*lengthRet = match.length();
|
||||
_lastMatch = match;
|
||||
return match.position();
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastMatch = NULL;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
catch(regex_error& /*ex*/)
|
||||
{
|
||||
// -1 is normally used for not found, -2 is used here for invalid regex
|
||||
return -2;
|
||||
}
|
||||
|
||||
// Work out the range of lines we're searching across, moving beyond an empty end-of-line
|
||||
int lineRangeStart = doc->LineFromPosition(startPos);
|
||||
int lineRangeEnd = doc->LineFromPosition(endPos);
|
||||
|
||||
regex_constants::match_flag_type flags(regex_constants::match_default);
|
||||
|
||||
|
||||
|
||||
// Work out the flags:
|
||||
if (startPos != doc->LineStart(lineRangeStart))
|
||||
{
|
||||
flags |= regex_constants::match_not_bol;
|
||||
}
|
||||
|
||||
if (endPos != doc->LineEnd(lineRangeEnd))
|
||||
{
|
||||
flags |= regex_constants::match_not_eol;
|
||||
}
|
||||
|
||||
if (0 == (searchFlags & SCFIND_REGEXP_DOTMATCHESNL))
|
||||
{
|
||||
flags |= regex_constants::match_not_dot_newline;
|
||||
}
|
||||
|
||||
int pos(-1);
|
||||
int lenRet(0);
|
||||
|
||||
|
||||
if (doc->CodePage() == SC_CP_UTF8)
|
||||
{
|
||||
UTF8DocumentIterator end(doc, endPos, endPos);
|
||||
bool success = boost::regex_search(UTF8DocumentIterator(doc, startPos, endPos), end, m_utf8match, m_wcharre, flags);
|
||||
if (success)
|
||||
{
|
||||
pos = m_utf8match[0].first.pos();
|
||||
lenRet = m_utf8match[0].second.pos() - pos;
|
||||
|
||||
if (increment == -1)
|
||||
{
|
||||
// Check for the last match on this line.
|
||||
int repetitions = 100; // Break out of infinite loop
|
||||
int previousPos = pos;
|
||||
while (success && ((pos + lenRet) <= endPos))
|
||||
{
|
||||
if (previousPos >= pos && 0 >= (--repetitions))
|
||||
break;
|
||||
previousPos = pos;
|
||||
success = regex_search(UTF8DocumentIterator(doc, pos + 1, endPos), end, m_utf8match, m_wcharre, flags);
|
||||
// success = regex_search(DocumentIterator(doc, pos + 1, endPos), end, match, re, static_cast<regex_constants::match_flag_type>(flags));
|
||||
if (success)
|
||||
{
|
||||
if ((pos + lenRet) <= minPos)
|
||||
{
|
||||
pos = m_utf8match[0].first.pos();
|
||||
lenRet = m_utf8match[0].second.pos() - pos;
|
||||
}
|
||||
else
|
||||
{
|
||||
success = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*length = lenRet;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AnsiDocumentIterator end(doc, endPos, endPos);
|
||||
|
||||
bool success = boost::regex_search(AnsiDocumentIterator(doc, startPos, endPos), end, m_ansimatch, m_charre, flags);
|
||||
if (success)
|
||||
{
|
||||
pos = m_ansimatch[0].first.pos();
|
||||
lenRet = m_ansimatch.length();
|
||||
|
||||
if (increment == -1)
|
||||
{
|
||||
// Check for the last match on this line.
|
||||
int repetitions = 100; // Break out of infinite loop
|
||||
int previousPos = pos;
|
||||
while (success && ((pos + lenRet) <= endPos))
|
||||
{
|
||||
if (previousPos >= pos && 0 >= (--repetitions))
|
||||
break;
|
||||
previousPos = pos;
|
||||
success = regex_search(AnsiDocumentIterator(doc, pos + 1, endPos), end, m_ansimatch, m_charre, flags);
|
||||
// success = regex_search(DocumentIterator(doc, pos + 1, endPos), end, match, re, static_cast<regex_constants::match_flag_type>(flags));
|
||||
if (success)
|
||||
{
|
||||
if ((pos + lenRet) <= minPos)
|
||||
{
|
||||
pos = m_ansimatch[0].first.pos();
|
||||
lenRet = m_ansimatch[0].length();
|
||||
}
|
||||
else
|
||||
{
|
||||
success = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*length = lenRet;
|
||||
}
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
template <class CharT, class CharacterIterator>
|
||||
BoostRegexSearch::Match BoostRegexSearch::EncodingDependent<CharT, CharacterIterator>::FindText(SearchParameters& search)
|
||||
{
|
||||
compileRegex(search._regexString, search._compileFlags);
|
||||
return (search._direction > 0)
|
||||
? FindTextForward(search)
|
||||
: FindTextBackward(search);
|
||||
}
|
||||
|
||||
template <class CharT, class CharacterIterator>
|
||||
BoostRegexSearch::Match BoostRegexSearch::EncodingDependent<CharT, CharacterIterator>::FindTextForward(SearchParameters& search)
|
||||
{
|
||||
CharacterIterator endIterator(search._document, search._endPosition, search._endPosition);
|
||||
int next_search_from_position = search._startPosition;
|
||||
bool found = false;
|
||||
bool match_is_valid = false;
|
||||
do {
|
||||
search._boostRegexFlags = search.isLineStart(next_search_from_position)
|
||||
? search._boostRegexFlags & ~regex_constants::match_not_bol
|
||||
: search._boostRegexFlags | regex_constants::match_not_bol;
|
||||
const bool end_reached = next_search_from_position > search._endPosition;
|
||||
found = !end_reached && boost::regex_search(CharacterIterator(search._document, next_search_from_position, search._endPosition), endIterator, _match, _regex, search._boostRegexFlags);
|
||||
if (found) {
|
||||
const int position = _match[0].first.pos();
|
||||
const int length = _match[0].second.pos() - position;
|
||||
const bool match_is_non_empty = length != 0;
|
||||
const bool is_allowed_empty_here = search._is_allowed_empty && (search._is_allowed_empty_at_start_position || position > search._startPosition);
|
||||
match_is_valid = match_is_non_empty || is_allowed_empty_here;
|
||||
if (!match_is_valid)
|
||||
next_search_from_position = search.nextCharacter(position);
|
||||
}
|
||||
} while (found && !match_is_valid);
|
||||
if (found)
|
||||
return Match(search._document, _match[0].first.pos(), _match[0].second.pos());
|
||||
else
|
||||
return Match();
|
||||
}
|
||||
|
||||
template <class CharT, class CharacterIterator>
|
||||
BoostRegexSearch::Match BoostRegexSearch::EncodingDependent<CharT, CharacterIterator>::FindTextBackward(SearchParameters& search)
|
||||
{
|
||||
// Change backward search into series of forward search. It is slow: search all backward becomes O(n^2) instead of O(n) (if search forward is O(n)).
|
||||
//NOTE: Maybe we should cache results. Maybe we could reverse regex to do a real backward search, for simple regex.
|
||||
search._direction = 1;
|
||||
const bool is_allowed_empty_at_end_position = search._is_allowed_empty_at_start_position;
|
||||
search._is_allowed_empty_at_start_position = search._is_allowed_empty;
|
||||
|
||||
MatchResults bestMatch;
|
||||
int bestPosition = -1;
|
||||
int bestEnd = -1;
|
||||
for (;;) {
|
||||
Match matchRange = FindText(search);
|
||||
if (!matchRange.found())
|
||||
break;
|
||||
int position = matchRange.position();
|
||||
int endPosition = matchRange.endPosition();
|
||||
if (endPosition > bestEnd && (endPosition < search._endPosition || position != endPosition || is_allowed_empty_at_end_position)) // We are searching for the longest match which has the fathest end (but may not accept empty match at end position).
|
||||
{
|
||||
bestMatch = _match;
|
||||
bestPosition = position;
|
||||
bestEnd = endPosition;
|
||||
}
|
||||
search._startPosition = search.nextCharacter(position);
|
||||
}
|
||||
if (bestPosition >= 0)
|
||||
return Match(search._document, bestPosition, bestEnd);
|
||||
else
|
||||
return Match();
|
||||
}
|
||||
|
||||
template <class CharT, class CharacterIterator>
|
||||
void BoostRegexSearch::EncodingDependent<CharT, CharacterIterator>::compileRegex(const char *regex, const int compileFlags)
|
||||
{
|
||||
if (_lastCompileFlags != compileFlags || _lastRegexString != regex)
|
||||
{
|
||||
_regex = Regex(CharTPtr(regex), static_cast<regex_constants::syntax_option_type>(compileFlags));
|
||||
_lastRegexString = regex;
|
||||
_lastCompileFlags = compileFlags;
|
||||
}
|
||||
}
|
||||
|
||||
int BoostRegexSearch::SearchParameters::nextCharacter(int position)
|
||||
{
|
||||
if (_skip_windows_line_end_as_one_character && _document->CharAt(position) == '\r' && _document->CharAt(position+1) == '\n')
|
||||
return position + 2;
|
||||
else
|
||||
return position + 1;
|
||||
}
|
||||
|
||||
bool BoostRegexSearch::SearchParameters::isLineStart(int position)
|
||||
{
|
||||
return (position == 0)
|
||||
|| _document->CharAt(position-1) == '\n'
|
||||
|| _document->CharAt(position-1) == '\r' && _document->CharAt(position) != '\n';
|
||||
}
|
||||
|
||||
bool BoostRegexSearch::SearchParameters::isLineEnd(int position)
|
||||
{
|
||||
return (position == _document->Length())
|
||||
|| _document->CharAt(position) == '\r'
|
||||
|| _document->CharAt(position) == '\n' && (position == 0 || _document->CharAt(position-1) != '\n');
|
||||
}
|
||||
|
||||
const char *BoostRegexSearch::SubstituteByPosition(Document* doc, const char *text, int *length) {
|
||||
delete []substituted;
|
||||
substituted = NULL;
|
||||
if (doc->CodePage() == SC_CP_UTF8)
|
||||
{
|
||||
const wchar_t* wtext = utf8ToWchar(text);
|
||||
std::wstring replaced = m_utf8match.format(wtext, boost::format_all);
|
||||
delete[] wtext;
|
||||
substituted = wcharToUtf8(replaced.c_str());
|
||||
*length = strlen(substituted);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string replaced = m_ansimatch.format(text, boost::format_all);
|
||||
*length = replaced.size();
|
||||
substituted = new char[*length + 1];
|
||||
strcpy(substituted, replaced.c_str());
|
||||
}
|
||||
delete[] _substituted;
|
||||
_substituted = (doc->CodePage() == SC_CP_UTF8)
|
||||
? _utf8.SubstituteByPosition(text, length)
|
||||
: _ansi.SubstituteByPosition(text, length);
|
||||
return _substituted;
|
||||
}
|
||||
|
||||
template <class CharT, class CharacterIterator>
|
||||
char *BoostRegexSearch::EncodingDependent<CharT, CharacterIterator>::SubstituteByPosition(const char *text, int *length) {
|
||||
char *substituted = stringToCharPtr(_match.format((const CharT*)CharTPtr(text), boost::format_all));
|
||||
*length = strlen(substituted);
|
||||
return substituted;
|
||||
}
|
||||
|
||||
@ -300,11 +446,10 @@ wchar_t *BoostRegexSearch::utf8ToWchar(const char *utf8)
|
||||
wchar_t *w = new wchar_t[wcharSize + 1];
|
||||
UTF16FromUTF8(utf8, utf8Size, w, wcharSize + 1);
|
||||
w[wcharSize] = 0;
|
||||
|
||||
return w;
|
||||
}
|
||||
|
||||
char* BoostRegexSearch::wcharToUtf8(const wchar_t *w)
|
||||
char *BoostRegexSearch::wcharToUtf8(const wchar_t *w)
|
||||
{
|
||||
int wcharSize = wcslen(w);
|
||||
int charSize = UTF8Length(w, wcharSize);
|
||||
@ -312,4 +457,15 @@ char* BoostRegexSearch::wcharToUtf8(const wchar_t *w)
|
||||
UTF8FromUTF16(w, wcharSize, c, charSize);
|
||||
c[charSize] = 0;
|
||||
return c;
|
||||
}
|
||||
|
||||
char *BoostRegexSearch::stringToCharPtr(const std::string& str)
|
||||
{
|
||||
char *charPtr = new char[str.length() + 1];
|
||||
strcpy(charPtr, str.c_str());
|
||||
return charPtr;
|
||||
}
|
||||
char *BoostRegexSearch::stringToCharPtr(const std::wstring& str)
|
||||
{
|
||||
return wcharToUtf8(str.c_str());
|
||||
}
|
@ -1,6 +1,12 @@
|
||||
#ifndef BOOSTREGEXSEARCH_H
|
||||
#define BOOSTREGEXSEARCH_H
|
||||
|
||||
#define SCFIND_REGEXP_DOTMATCHESNL 0x10000000
|
||||
#define SCFIND_REGEXP_DOTMATCHESNL 0x10000000
|
||||
#define SCFIND_REGEXP_EMPTYMATCH_MASK 0xE0000000
|
||||
#define SCFIND_REGEXP_EMPTYMATCH_NONE 0x00000000
|
||||
#define SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH 0x20000000
|
||||
#define SCFIND_REGEXP_EMPTYMATCH_ALL 0x40000000
|
||||
#define SCFIND_REGEXP_EMPTYMATCH_ALLOWATSTART 0x80000000
|
||||
#define SCFIND_REGEXP_SKIPCRLFASONE 0x08000000
|
||||
|
||||
#endif
|
@ -834,6 +834,13 @@ val SCFIND_MATCHCASE=4
|
||||
val SCFIND_WORDSTART=0x00100000
|
||||
val SCFIND_REGEXP=0x00200000
|
||||
val SCFIND_POSIX=0x00400000
|
||||
val SCFIND_REGEXP_DOTMATCHESNL=0x10000000
|
||||
val SCFIND_REGEXP_EMPTYMATCH_MASK=0xE0000000
|
||||
val SCFIND_REGEXP_EMPTYMATCH_NONE=0x00000000
|
||||
val SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH=0x20000000
|
||||
val SCFIND_REGEXP_EMPTYMATCH_ALL=0x40000000
|
||||
val SCFIND_REGEXP_EMPTYMATCH_ALLOWATSTART=0x80000000
|
||||
val SCFIND_REGEXP_SKIPCRLFASONE=0x08000000
|
||||
|
||||
# Find some text in the document.
|
||||
fun position FindText=2150(int flags, findtext ft)
|
||||
|
63
scintilla/test/MessageNumbers.py
Normal file
63
scintilla/test/MessageNumbers.py
Normal file
@ -0,0 +1,63 @@
|
||||
# List many windows message numbers
|
||||
|
||||
msgs = {
|
||||
"WM_ACTIVATE":6,
|
||||
"WM_ACTIVATEAPP":28,
|
||||
"WM_CAPTURECHANGED":533,
|
||||
"WM_CHAR":258,
|
||||
"WM_CLOSE":16,
|
||||
"WM_CREATE":1,
|
||||
"WM_COMMAND":273,
|
||||
"WM_DESTROY":2,
|
||||
"WM_ENTERSIZEMOVE":561,
|
||||
"WM_ERASEBKGND":20,
|
||||
"WM_EXITSIZEMOVE":562,
|
||||
"WM_GETMINMAXINFO":36,
|
||||
"WM_GETTEXT":13,
|
||||
"WM_IME_SETCONTEXT":0x0281,
|
||||
"WM_IME_NOTIFY":0x0282,
|
||||
"WM_KEYDOWN":256,
|
||||
"WM_KEYUP":257,
|
||||
"WM_KILLFOCUS":8,
|
||||
"WM_LBUTTONDOWN":513,
|
||||
"WM_LBUTTONUP":514,
|
||||
"WM_MBUTTONDOWN":519,
|
||||
"WM_MBUTTONUP":520,
|
||||
"WM_MBUTTONDBLCLK":521,
|
||||
"WM_MOUSEACTIVATE":33,
|
||||
"WM_MOUSEMOVE":512,
|
||||
"WM_MOVE":3,
|
||||
"WM_MOVING":534,
|
||||
"WM_NCACTIVATE":134,
|
||||
"WM_NCCALCSIZE":131,
|
||||
"WM_NCCREATE":129,
|
||||
"WM_NCDESTROY":130,
|
||||
"WM_NCHITTEST":132,
|
||||
"WM_NCLBUTTONDBLCLK":163,
|
||||
"WM_NCLBUTTONDOWN":161,
|
||||
"WM_NCLBUTTONUP":162,
|
||||
"WM_NCMOUSEMOVE":160,
|
||||
"WM_NCPAINT":133,
|
||||
"WM_PAINT":15,
|
||||
"WM_PARENTNOTIFY":528,
|
||||
"WM_SETCURSOR":32,
|
||||
"WM_SETFOCUS":7,
|
||||
"WM_SETFONT":48,
|
||||
"WM_SETTEXT":12,
|
||||
"WM_SHOWWINDOW":24,
|
||||
"WM_SIZE":5,
|
||||
"WM_SIZING":532,
|
||||
"WM_SYNCPAINT":136,
|
||||
"WM_SYSCOMMAND":274,
|
||||
"WM_SYSKEYDOWN":260,
|
||||
"WM_TIMER":275,
|
||||
"WM_USER":1024,
|
||||
"WM_USER+1":1025,
|
||||
"WM_WINDOWPOSCHANGED":71,
|
||||
"WM_WINDOWPOSCHANGING":70,
|
||||
}
|
||||
|
||||
sgsm={}
|
||||
for k,v in msgs.items():
|
||||
sgsm[v] = k
|
||||
|
31
scintilla/test/README
Normal file
31
scintilla/test/README
Normal file
@ -0,0 +1,31 @@
|
||||
The test directory contains some unit and performance tests for Scintilla.
|
||||
|
||||
The tests can only be run on Windows using Python 3.x. Running on another platform
|
||||
would require writing a file similar to XiteWin.py for that platform. Python 3.x is required
|
||||
because its default string type is Unicode and earlier Python versions use byte strings
|
||||
and the interface to the platform assumes a particular string type.
|
||||
|
||||
A test application is in xite.py and this can be run to experiment:
|
||||
pythonw xite.py
|
||||
|
||||
To run the basic tests:
|
||||
pythonw simpleTests.py
|
||||
|
||||
There are some lexing tests with simple input files in several languages in the examples
|
||||
subdirectory and their expected lexed states in *.styled where the start of each style
|
||||
is marked with {styleNumber}, for example:
|
||||
{15}<%@{16}language=javas{15}%>{0}
|
||||
|
||||
To run the lexing tests:
|
||||
pythonw lexTests.py
|
||||
|
||||
To check for performance regressions:
|
||||
pythonw performanceTests.py
|
||||
While each test run will be different and the timer has only limited granularity, some results
|
||||
from a 2 GHz Athlon with a DEBUG build are:
|
||||
0.187 testAddLine
|
||||
. 0.203 testAddLineMiddle
|
||||
. 0.171 testHuge
|
||||
. 0.203 testHugeInserts
|
||||
. 0.312 testHugeReplace
|
||||
.
|
26
scintilla/test/XiteMenu.py
Normal file
26
scintilla/test/XiteMenu.py
Normal file
@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
""" Define the menu structure used by the Pentacle applications """
|
||||
|
||||
MenuStructure = [
|
||||
["&File", [
|
||||
["&New", "<control>N"],
|
||||
["&Open...", "<control>O"],
|
||||
["&Save", "<control>S"],
|
||||
["Save &As...", "<control><shift>S"],
|
||||
["Test", ""],
|
||||
["Exercised", ""],
|
||||
["Uncalled", ""],
|
||||
["-", ""],
|
||||
["&Exit", ""]]],
|
||||
[ "&Edit", [
|
||||
["&Undo", "<control>Z"],
|
||||
["&Redo", "<control>Y"],
|
||||
["-", ""],
|
||||
["Cu&t", "<control>X"],
|
||||
["&Copy", "<control>C"],
|
||||
["&Paste", "<control>V"],
|
||||
["&Delete", "Del"],
|
||||
["Select &All", "<control>A"],
|
||||
]],
|
||||
]
|
666
scintilla/test/XiteWin.py
Normal file
666
scintilla/test/XiteWin.py
Normal file
@ -0,0 +1,666 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os, sys, unittest
|
||||
|
||||
import ctypes
|
||||
from ctypes import wintypes
|
||||
from ctypes import c_int, c_ulong, c_char_p, c_wchar_p, c_ushort
|
||||
user32=ctypes.windll.user32
|
||||
gdi32=ctypes.windll.gdi32
|
||||
kernel32=ctypes.windll.kernel32
|
||||
from MessageNumbers import msgs, sgsm
|
||||
|
||||
import XiteMenu
|
||||
|
||||
scintillaDirectory = ".."
|
||||
scintillaIncludeDirectory = os.path.join(scintillaDirectory, "include")
|
||||
sys.path.append(scintillaIncludeDirectory)
|
||||
import Face
|
||||
|
||||
scintillaBinDirectory = os.path.join(scintillaDirectory, "bin")
|
||||
os.environ['PATH'] = os.environ['PATH'] + ";" + scintillaBinDirectory
|
||||
#print(os.environ['PATH'])
|
||||
|
||||
WFUNC = ctypes.WINFUNCTYPE(c_int, c_int, c_int, c_int, c_int)
|
||||
|
||||
WS_CHILD = 0x40000000
|
||||
WS_CLIPCHILDREN = 0x2000000
|
||||
WS_OVERLAPPEDWINDOW = 0xcf0000
|
||||
WS_VISIBLE = 0x10000000
|
||||
WS_HSCROLL = 0x100000
|
||||
WS_VSCROLL = 0x200000
|
||||
WA_INACTIVE = 0
|
||||
MF_POPUP = 16
|
||||
MF_SEPARATOR = 0x800
|
||||
IDYES = 6
|
||||
OFN_HIDEREADONLY = 4
|
||||
MB_OK = 0
|
||||
MB_YESNOCANCEL = 3
|
||||
MF_CHECKED = 8
|
||||
MF_UNCHECKED = 0
|
||||
SW_SHOW = 5
|
||||
PM_REMOVE = 1
|
||||
|
||||
VK_SHIFT = 16
|
||||
VK_CONTROL = 17
|
||||
VK_MENU = 18
|
||||
|
||||
class OPENFILENAME(ctypes.Structure):
|
||||
_fields_ = (("lStructSize", c_int),
|
||||
("hwndOwner", c_int),
|
||||
("hInstance", c_int),
|
||||
("lpstrFilter", c_wchar_p),
|
||||
("lpstrCustomFilter", c_char_p),
|
||||
("nMaxCustFilter", c_int),
|
||||
("nFilterIndex", c_int),
|
||||
("lpstrFile", c_wchar_p),
|
||||
("nMaxFile", c_int),
|
||||
("lpstrFileTitle", c_wchar_p),
|
||||
("nMaxFileTitle", c_int),
|
||||
("lpstrInitialDir", c_wchar_p),
|
||||
("lpstrTitle", c_wchar_p),
|
||||
("flags", c_int),
|
||||
("nFileOffset", c_ushort),
|
||||
("nFileExtension", c_ushort),
|
||||
("lpstrDefExt", c_char_p),
|
||||
("lCustData", c_int),
|
||||
("lpfnHook", c_char_p),
|
||||
("lpTemplateName", c_char_p),
|
||||
("pvReserved", c_char_p),
|
||||
("dwReserved", c_int),
|
||||
("flagsEx", c_int))
|
||||
|
||||
def __init__(self, win, title):
|
||||
ctypes.Structure.__init__(self)
|
||||
self.lStructSize = ctypes.sizeof(OPENFILENAME)
|
||||
self.nMaxFile = 1024
|
||||
self.hwndOwner = win
|
||||
self.lpstrTitle = title
|
||||
self.Flags = OFN_HIDEREADONLY
|
||||
|
||||
trace = False
|
||||
#~ trace = True
|
||||
|
||||
def WindowSize(w):
|
||||
rc = ctypes.wintypes.RECT()
|
||||
user32.GetClientRect(w, ctypes.byref(rc))
|
||||
return rc.right - rc.left, rc.bottom - rc.top
|
||||
|
||||
def IsKeyDown(key):
|
||||
return (user32.GetKeyState(key) & 0x8000) != 0
|
||||
|
||||
def KeyTranslate(w):
|
||||
tr = { 9: "Tab", 0xD:"Enter", 0x1B: "Esc" }
|
||||
if w in tr:
|
||||
return tr[w]
|
||||
elif ord("A") <= w <= ord("Z"):
|
||||
return chr(w)
|
||||
elif 0x70 <= w <= 0x7b:
|
||||
return "F" + str(w-0x70+1)
|
||||
else:
|
||||
return "Unknown_" + hex(w)
|
||||
|
||||
class WNDCLASS(ctypes.Structure):
|
||||
_fields_= (\
|
||||
('style', c_int),
|
||||
('lpfnWndProc', WFUNC),
|
||||
('cls_extra', c_int),
|
||||
('wnd_extra', c_int),
|
||||
('hInst', c_int),
|
||||
('hIcon', c_int),
|
||||
('hCursor', c_int),
|
||||
('hbrBackground', c_int),
|
||||
('menu_name', c_wchar_p),
|
||||
('lpzClassName', c_wchar_p),
|
||||
)
|
||||
|
||||
class XTEXTRANGE(ctypes.Structure):
|
||||
_fields_= (\
|
||||
('cpMin', c_int),
|
||||
('cpMax', c_int),
|
||||
('lpstrText', c_char_p),
|
||||
)
|
||||
|
||||
class TEXTRANGE(ctypes.Structure):
|
||||
_fields_= (\
|
||||
('cpMin', c_int),
|
||||
('cpMax', c_int),
|
||||
('lpstrText', ctypes.POINTER(ctypes.c_char)),
|
||||
)
|
||||
|
||||
class FINDTEXT(ctypes.Structure):
|
||||
_fields_= (\
|
||||
('cpMin', c_int),
|
||||
('cpMax', c_int),
|
||||
('lpstrText', c_char_p),
|
||||
('cpMinText', c_int),
|
||||
('cpMaxText', c_int),
|
||||
)
|
||||
|
||||
hinst = ctypes.windll.kernel32.GetModuleHandleW(0)
|
||||
|
||||
def RegisterClass(name, func, background = 0):
|
||||
# register a window class for toplevel windows.
|
||||
wc = WNDCLASS()
|
||||
wc.style = 0
|
||||
wc.lpfnWndProc = func
|
||||
wc.cls_extra = 0
|
||||
wc.wnd_extra = 0
|
||||
wc.hInst = hinst
|
||||
wc.hIcon = 0
|
||||
wc.hCursor = 0
|
||||
wc.hbrBackground = background
|
||||
wc.menu_name = 0
|
||||
wc.lpzClassName = name
|
||||
user32.RegisterClassW(ctypes.byref(wc))
|
||||
|
||||
class SciCall:
|
||||
def __init__(self, fn, ptr, msg):
|
||||
self._fn = fn
|
||||
self._ptr = ptr
|
||||
self._msg = msg
|
||||
def __call__(self, w=0, l=0):
|
||||
return self._fn(self._ptr, self._msg, w, l)
|
||||
|
||||
class Scintilla:
|
||||
def __init__(self, face, hwndParent, hinstance):
|
||||
self.__dict__["face"] = face
|
||||
self.__dict__["used"] = set()
|
||||
self.__dict__["all"] = set()
|
||||
# The k member is for accessing constants as a dictionary
|
||||
self.__dict__["k"] = {}
|
||||
for f in face.features:
|
||||
self.all.add(f)
|
||||
if face.features[f]["FeatureType"] == "val":
|
||||
self.k[f] = int(self.face.features[f]["Value"], 0)
|
||||
elif face.features[f]["FeatureType"] == "evt":
|
||||
self.k["SCN_"+f] = int(self.face.features[f]["Value"], 0)
|
||||
# Get the function first as that also loads the DLL
|
||||
import ctypes.util
|
||||
print >> sys.stderr, "SciLexer path: ",ctypes.util.find_library("scilexer")
|
||||
self.__dict__["_scifn"] = ctypes.windll.SciLexer.Scintilla_DirectFunction
|
||||
self.__dict__["_hwnd"] = user32.CreateWindowExW(0,
|
||||
"Scintilla", "Source",
|
||||
WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_CLIPCHILDREN,
|
||||
0, 0, 100, 100, hwndParent, 0, hinstance, 0)
|
||||
self.__dict__["_sciptr"] = user32.SendMessageW(self._hwnd,
|
||||
int(self.face.features["GetDirectPointer"]["Value"], 0), 0,0)
|
||||
user32.ShowWindow(self._hwnd, SW_SHOW)
|
||||
def __getattr__(self, name):
|
||||
if name in self.face.features:
|
||||
self.used.add(name)
|
||||
feature = self.face.features[name]
|
||||
value = int(feature["Value"], 0)
|
||||
#~ print("Feature", name, feature)
|
||||
if feature["FeatureType"] == "val":
|
||||
self.__dict__[name] = value
|
||||
return value
|
||||
else:
|
||||
return SciCall(self._scifn, self._sciptr, value)
|
||||
elif ("Get" + name) in self.face.features:
|
||||
self.used.add("Get" + name)
|
||||
feature = self.face.features["Get" + name]
|
||||
value = int(feature["Value"], 0)
|
||||
if feature["FeatureType"] == "get" and \
|
||||
not name.startswith("Get") and \
|
||||
not feature["Param1Type"] and \
|
||||
not feature["Param2Type"] and \
|
||||
feature["ReturnType"] in ["bool", "int", "position"]:
|
||||
#~ print("property", feature)
|
||||
return self._scifn(self._sciptr, value, 0, 0)
|
||||
elif name.startswith("SCN_") and name in self.k:
|
||||
self.used.add(name)
|
||||
feature = self.face.features[name[4:]]
|
||||
value = int(feature["Value"], 0)
|
||||
#~ print("Feature", name, feature)
|
||||
if feature["FeatureType"] == "val":
|
||||
return value
|
||||
raise AttributeError(name)
|
||||
def __setattr__(self, name, val):
|
||||
if ("Set" + name) in self.face.features:
|
||||
self.used.add("Set" + name)
|
||||
feature = self.face.features["Set" + name]
|
||||
value = int(feature["Value"], 0)
|
||||
#~ print("setproperty", feature)
|
||||
if feature["FeatureType"] == "set" and not name.startswith("Set"):
|
||||
if feature["Param1Type"] in ["bool", "int", "position"]:
|
||||
return self._scifn(self._sciptr, value, val, 0)
|
||||
elif feature["Param2Type"] in ["string"]:
|
||||
return self._scifn(self._sciptr, value, 0, val)
|
||||
raise AttributeError(name)
|
||||
raise AttributeError(name)
|
||||
def getvalue(self, name):
|
||||
if name in self.face.features:
|
||||
feature = self.face.features[name]
|
||||
if feature["FeatureType"] != "evt":
|
||||
try:
|
||||
return int(feature["Value"], 0)
|
||||
except ValueError:
|
||||
return -1
|
||||
return -1
|
||||
|
||||
|
||||
def ByteRange(self, start, end):
|
||||
tr = TEXTRANGE()
|
||||
tr.cpMin = start
|
||||
tr.cpMax = end
|
||||
length = end - start
|
||||
tr.lpstrText = ctypes.create_string_buffer(length + 1)
|
||||
self.GetTextRange(0, ctypes.byref(tr))
|
||||
text = tr.lpstrText[:length]
|
||||
text += b"\0" * (length - len(text))
|
||||
return text
|
||||
def StyledTextRange(self, start, end):
|
||||
tr = TEXTRANGE()
|
||||
tr.cpMin = start
|
||||
tr.cpMax = end
|
||||
length = 2 * (end - start)
|
||||
tr.lpstrText = ctypes.create_string_buffer(length + 2)
|
||||
self.GetStyledText(0, ctypes.byref(tr))
|
||||
styledText = tr.lpstrText[:length]
|
||||
styledText += b"\0" * (length - len(styledText))
|
||||
return styledText
|
||||
def FindBytes(self, start, end, s, flags):
|
||||
ft = FINDTEXT()
|
||||
ft.cpMin = start
|
||||
ft.cpMax = end
|
||||
ft.lpstrText = s
|
||||
ft.cpMinText = 0
|
||||
ft.cpMaxText = 0
|
||||
pos = self.FindText(flags, ctypes.byref(ft))
|
||||
#~ print(start, end, ft.cpMinText, ft.cpMaxText)
|
||||
return pos
|
||||
def FindBytes2(self, start, end, s, flags):
|
||||
ft = FINDTEXT()
|
||||
ft.cpMin = start
|
||||
ft.cpMax = end
|
||||
ft.lpstrText = s
|
||||
ft.cpMinText = 0
|
||||
ft.cpMaxText = 0
|
||||
pos = self.FindText(flags, ctypes.byref(ft))
|
||||
#~ print(start, end, ft.cpMinText, ft.cpMaxText)
|
||||
return (pos, ft.cpMinText, ft.cpMaxText)
|
||||
|
||||
def Contents(self):
|
||||
return self.ByteRange(0, self.Length)
|
||||
def SizeTo(self, width, height):
|
||||
user32.SetWindowPos(self._hwnd, 0, 0, 0, width, height, 0)
|
||||
def FocusOn(self):
|
||||
user32.SetFocus(self._hwnd)
|
||||
|
||||
class XiteWin():
|
||||
def __init__(self, test=""):
|
||||
self.face = Face.Face()
|
||||
self.face.ReadFromFile(os.path.join(scintillaIncludeDirectory, "Scintilla.iface"))
|
||||
|
||||
self.titleDirty = True
|
||||
self.fullPath = ""
|
||||
self.test = test
|
||||
|
||||
self.appName = "xite"
|
||||
|
||||
self.cmds = {}
|
||||
self.windowName = "XiteWindow"
|
||||
self.wfunc = WFUNC(self.WndProc)
|
||||
RegisterClass(self.windowName, self.wfunc)
|
||||
user32.CreateWindowExW(0, self.windowName, self.appName, \
|
||||
WS_VISIBLE | WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, \
|
||||
0, 0, 500, 700, 0, 0, hinst, 0)
|
||||
|
||||
args = sys.argv[1:]
|
||||
self.SetMenus()
|
||||
if args:
|
||||
self.GrabFile(args[0])
|
||||
self.ed.FocusOn()
|
||||
self.ed.GotoPos(self.ed.Length)
|
||||
|
||||
if self.test:
|
||||
print(self.test)
|
||||
for k in self.cmds:
|
||||
if self.cmds[k] == "Test":
|
||||
user32.PostMessageW(self.win, msgs["WM_COMMAND"], k, 0)
|
||||
|
||||
def OnSize(self):
|
||||
width, height = WindowSize(self.win)
|
||||
self.ed.SizeTo(width, height)
|
||||
user32.InvalidateRect(self.win, 0, 0)
|
||||
|
||||
def OnCreate(self, hwnd):
|
||||
self.win = hwnd
|
||||
self.ed = Scintilla(self.face, hwnd, hinst)
|
||||
self.ed.FocusOn()
|
||||
|
||||
|
||||
def Invalidate(self):
|
||||
user32.InvalidateRect(self.win, 0, 0)
|
||||
|
||||
def WndProc(self, h, m, w, l):
|
||||
ms = sgsm.get(m, "XXX")
|
||||
if trace:
|
||||
print("%s %s %s %s" % (hex(h)[2:],ms,w,l))
|
||||
if ms == "WM_CLOSE":
|
||||
user32.PostQuitMessage(0)
|
||||
elif ms == "WM_CREATE":
|
||||
self.OnCreate(h)
|
||||
return 0
|
||||
elif ms == "WM_SIZE":
|
||||
# Work out size
|
||||
if w != 1:
|
||||
self.OnSize()
|
||||
return 0
|
||||
elif ms == "WM_COMMAND":
|
||||
cmdCode = w & 0xffff
|
||||
if cmdCode in self.cmds:
|
||||
self.Command(self.cmds[cmdCode])
|
||||
return 0
|
||||
elif ms == "WM_ACTIVATE":
|
||||
if w != WA_INACTIVE:
|
||||
self.ed.FocusOn()
|
||||
return 0
|
||||
else:
|
||||
return user32.DefWindowProcW(h, m, w, l)
|
||||
return 0
|
||||
|
||||
def Command(self, name):
|
||||
name = name.replace(" ", "")
|
||||
method = "Cmd" + name
|
||||
cmd = None
|
||||
try:
|
||||
cmd = getattr(self, method)
|
||||
except AttributeError:
|
||||
return
|
||||
if cmd:
|
||||
cmd()
|
||||
|
||||
def KeyDown(self, w, prefix = ""):
|
||||
keyName = prefix
|
||||
if IsKeyDown(VK_CONTROL):
|
||||
keyName += "<control>"
|
||||
if IsKeyDown(VK_SHIFT):
|
||||
keyName += "<shift>"
|
||||
keyName += KeyTranslate(w)
|
||||
if trace:
|
||||
print("Key:", keyName)
|
||||
if keyName in self.keys:
|
||||
method = "Cmd" + self.keys[keyName]
|
||||
getattr(self, method)()
|
||||
return True
|
||||
#~ print("UKey:", keyName)
|
||||
return False
|
||||
|
||||
def Accelerator(self, msg):
|
||||
ms = sgsm.get(msg.message, "XXX")
|
||||
if ms == "WM_KEYDOWN":
|
||||
return self.KeyDown(msg.wParam)
|
||||
elif ms == "WM_SYSKEYDOWN":
|
||||
return self.KeyDown(msg.wParam, "<alt>")
|
||||
return False
|
||||
|
||||
def AppLoop(self):
|
||||
msg = ctypes.wintypes.MSG()
|
||||
lpmsg = ctypes.byref(msg)
|
||||
while user32.GetMessageW(lpmsg, 0, 0, 0):
|
||||
if trace and msg.message != msgs["WM_TIMER"]:
|
||||
print('mm', hex(msg.hWnd)[2:],sgsm.get(msg.message, "XXX"))
|
||||
if not self.Accelerator(msg):
|
||||
user32.TranslateMessage(lpmsg)
|
||||
user32.DispatchMessageW(lpmsg)
|
||||
|
||||
def DoEvents(self):
|
||||
msg = ctypes.wintypes.MSG()
|
||||
lpmsg = ctypes.byref(msg)
|
||||
cont = True
|
||||
while cont:
|
||||
cont = user32.PeekMessageW(lpmsg, 0, 0, 0, PM_REMOVE)
|
||||
if cont:
|
||||
if not self.Accelerator(msg):
|
||||
user32.TranslateMessage(lpmsg)
|
||||
user32.DispatchMessageW(lpmsg)
|
||||
|
||||
def SetTitle(self, changePath):
|
||||
if changePath or self.titleDirty != self.ed.Modify:
|
||||
self.titleDirty = self.ed.Modify
|
||||
self.title = self.fullPath
|
||||
if self.titleDirty:
|
||||
self.title += " * "
|
||||
else:
|
||||
self.title += " - "
|
||||
self.title += self.appName
|
||||
if self.win:
|
||||
user32.SetWindowTextW(self.win, self.title)
|
||||
|
||||
def Open(self):
|
||||
ofx = OPENFILENAME(self.win, "Open File")
|
||||
opath = "\0" * 1024
|
||||
ofx.lpstrFile = opath
|
||||
filters = ["Python (.py;.pyw)|*.py;*.pyw|All|*.*"]
|
||||
filterText = "\0".join([f.replace("|", "\0") for f in filters])+"\0\0"
|
||||
ofx.lpstrFilter = filterText
|
||||
if ctypes.windll.comdlg32.GetOpenFileNameW(ctypes.byref(ofx)):
|
||||
absPath = opath.replace("\0", "")
|
||||
self.GrabFile(absPath)
|
||||
self.ed.FocusOn()
|
||||
self.ed.LexerLanguage = "python"
|
||||
self.ed.Lexer = self.ed.SCLEX_PYTHON
|
||||
self.ed.SetKeyWords(0, b"class def else for from if import print return while")
|
||||
for style in [k for k in self.ed.k if k.startswith("SCE_P_")]:
|
||||
self.ed.StyleSetFont(self.ed.k[style], b"Verdana")
|
||||
if "COMMENT" in style:
|
||||
self.ed.StyleSetFore(self.ed.k[style], 127 * 256)
|
||||
self.ed.StyleSetFont(self.ed.k[style], b"Comic Sans MS")
|
||||
elif "OPERATOR" in style:
|
||||
self.ed.StyleSetBold(self.ed.k[style], 1)
|
||||
self.ed.StyleSetFore(self.ed.k[style], 127 * 256 * 256)
|
||||
elif "WORD" in style:
|
||||
self.ed.StyleSetItalic(self.ed.k[style], 255)
|
||||
self.ed.StyleSetFore(self.ed.k[style], 255 * 256 * 256)
|
||||
elif "TRIPLE" in style:
|
||||
self.ed.StyleSetFore(self.ed.k[style], 0xA0A0)
|
||||
elif "STRING" in style or "CHARACTER" in style:
|
||||
self.ed.StyleSetFore(self.ed.k[style], 0xA000A0)
|
||||
else:
|
||||
self.ed.StyleSetFore(self.ed.k[style], 0)
|
||||
|
||||
def SaveAs(self):
|
||||
ofx = OPENFILENAME(self.win, "Save File")
|
||||
opath = "\0" * 1024
|
||||
ofx.lpstrFile = opath
|
||||
if ctypes.windll.comdlg32.GetSaveFileNameW(ctypes.byref(ofx)):
|
||||
self.fullPath = opath.replace("\0", "")
|
||||
self.Save()
|
||||
self.SetTitle(1)
|
||||
self.ed.FocusOn()
|
||||
|
||||
def SetMenus(self):
|
||||
ui = XiteMenu.MenuStructure
|
||||
self.cmds = {}
|
||||
self.keys = {}
|
||||
|
||||
cmdId = 0
|
||||
self.menuBar = user32.CreateMenu()
|
||||
for name, contents in ui:
|
||||
cmdId += 1
|
||||
menu = user32.CreateMenu()
|
||||
for item in contents:
|
||||
text, key = item
|
||||
cmdText = text.replace("&", "")
|
||||
cmdText = cmdText.replace("...", "")
|
||||
cmdText = cmdText.replace(" ", "")
|
||||
cmdId += 1
|
||||
if key:
|
||||
keyText = key.replace("<control>", "Ctrl+")
|
||||
keyText = keyText.replace("<shift>", "Shift+")
|
||||
text += "\t" + keyText
|
||||
if text == "-":
|
||||
user32.AppendMenuW(menu, MF_SEPARATOR, cmdId, text)
|
||||
else:
|
||||
user32.AppendMenuW(menu, 0, cmdId, text)
|
||||
self.cmds[cmdId] = cmdText
|
||||
self.keys[key] = cmdText
|
||||
#~ print(cmdId, item)
|
||||
user32.AppendMenuW(self.menuBar, MF_POPUP, menu, name)
|
||||
user32.SetMenu(self.win, self.menuBar)
|
||||
self.CheckMenuItem("Wrap", True)
|
||||
user32.ShowWindow(self.win, SW_SHOW)
|
||||
|
||||
def CheckMenuItem(self, name, val):
|
||||
#~ print(name, val)
|
||||
if self.cmds:
|
||||
for k,v in self.cmds.items():
|
||||
if v == name:
|
||||
#~ print(name, k)
|
||||
user32.CheckMenuItem(user32.GetMenu(self.win), \
|
||||
k, [MF_UNCHECKED, MF_CHECKED][val])
|
||||
|
||||
def Exit(self):
|
||||
sys.exit(0)
|
||||
|
||||
def DisplayMessage(self, msg, ask):
|
||||
return IDYES == user32.MessageBoxW(self.win, \
|
||||
msg, self.appName, [MB_OK, MB_YESNOCANCEL][ask])
|
||||
|
||||
def NewDocument(self):
|
||||
self.ed.ClearAll()
|
||||
self.ed.EmptyUndoBuffer()
|
||||
self.ed.SetSavePoint()
|
||||
|
||||
def SaveIfUnsure(self):
|
||||
if self.ed.Modify:
|
||||
msg = "Save changes to \"" + self.fullPath + "\"?"
|
||||
print(msg)
|
||||
decision = self.DisplayMessage(msg, True)
|
||||
if decision:
|
||||
self.CmdSave()
|
||||
return decision
|
||||
return True
|
||||
|
||||
def New(self):
|
||||
if self.SaveIfUnsure():
|
||||
self.fullPath = ""
|
||||
self.overrideMode = None
|
||||
self.NewDocument()
|
||||
self.SetTitle(1)
|
||||
self.Invalidate()
|
||||
|
||||
def CheckMenus(self):
|
||||
pass
|
||||
|
||||
def MoveSelection(self, caret, anchor=-1):
|
||||
if anchor == -1:
|
||||
anchor = caret
|
||||
self.ed.SetSelectionStart(caret)
|
||||
self.ed.SetSelectionEnd(anchor)
|
||||
self.ed.ScrollCaret()
|
||||
self.Invalidate()
|
||||
|
||||
def GrabFile(self, name):
|
||||
self.fullPath = name
|
||||
self.overrideMode = None
|
||||
self.NewDocument()
|
||||
fsr = open(name, "rb")
|
||||
data = fsr.read()
|
||||
fsr.close()
|
||||
self.ed.AddText(len(data), data)
|
||||
self.ed.EmptyUndoBuffer()
|
||||
self.MoveSelection(0)
|
||||
self.SetTitle(1)
|
||||
|
||||
def Save(self):
|
||||
fos = open(self.fullPath, "wb")
|
||||
blockSize = 1024
|
||||
length = self.ed.Length
|
||||
i = 0
|
||||
while i < length:
|
||||
grabSize = length - i
|
||||
if grabSize > blockSize:
|
||||
grabSize = blockSize
|
||||
#~ print(i, grabSize, length)
|
||||
data = self.ed.ByteRange(i, i + grabSize)
|
||||
fos.write(data)
|
||||
i += grabSize
|
||||
fos.close()
|
||||
self.ed.SetSavePoint()
|
||||
self.SetTitle(0)
|
||||
|
||||
# Command handlers are called by menu actions
|
||||
|
||||
def CmdNew(self):
|
||||
self.New()
|
||||
|
||||
def CmdOpen(self):
|
||||
self.Open()
|
||||
|
||||
def CmdSave(self):
|
||||
if (self.fullPath == None) or (len(self.fullPath) == 0):
|
||||
self.SaveAs()
|
||||
else:
|
||||
self.Save()
|
||||
|
||||
def CmdSaveAs(self):
|
||||
self.SaveAs()
|
||||
|
||||
def CmdTest(self):
|
||||
runner = unittest.TextTestRunner()
|
||||
if self.test:
|
||||
tests = unittest.defaultTestLoader.loadTestsFromName(self.test)
|
||||
else:
|
||||
tests = unittest.defaultTestLoader.loadTestsFromName("simpleTests")
|
||||
results = runner.run(tests)
|
||||
#~ print(results)
|
||||
if self.test:
|
||||
user32.PostQuitMessage(0)
|
||||
|
||||
def CmdExercised(self):
|
||||
print()
|
||||
unused = sorted(self.ed.all.difference(self.ed.used))
|
||||
print("Unused", len(unused))
|
||||
print()
|
||||
print("\n".join(unused))
|
||||
print()
|
||||
print("Used", len(self.ed.used))
|
||||
print()
|
||||
print("\n".join(sorted(self.ed.used)))
|
||||
|
||||
def Uncalled(self):
|
||||
print("")
|
||||
unused = sorted(self.ed.all.difference(self.ed.used))
|
||||
uu = {}
|
||||
for u in unused:
|
||||
v = self.ed.getvalue(u)
|
||||
if v > 2000:
|
||||
uu[v] = u
|
||||
#~ for x in sorted(uu.keys())[150:]:
|
||||
return uu
|
||||
|
||||
def CmdExit(self):
|
||||
self.Exit()
|
||||
|
||||
def CmdUndo(self):
|
||||
self.ed.Undo()
|
||||
|
||||
def CmdRedo(self):
|
||||
self.ed.Redo()
|
||||
|
||||
def CmdCut(self):
|
||||
self.ed.Cut()
|
||||
|
||||
def CmdCopy(self):
|
||||
self.ed.Copy()
|
||||
|
||||
def CmdPaste(self):
|
||||
self.ed.Paste()
|
||||
|
||||
def CmdDelete(self):
|
||||
self.ed.Clear()
|
||||
|
||||
xiteFrame = None
|
||||
|
||||
def main(test):
|
||||
global xiteFrame
|
||||
xiteFrame = XiteWin(test)
|
||||
xiteFrame.AppLoop()
|
||||
#~ xiteFrame.CmdExercised()
|
||||
return xiteFrame.Uncalled()
|
12
scintilla/test/examples/x.asp
Normal file
12
scintilla/test/examples/x.asp
Normal file
@ -0,0 +1,12 @@
|
||||
<%@language=javas%>
|
||||
<%
|
||||
#include
|
||||
function x() {
|
||||
}
|
||||
%>
|
||||
<%@language=vbscript%>
|
||||
<%
|
||||
sub x 'comment
|
||||
%>
|
||||
<head>
|
||||
<body></body>
|
12
scintilla/test/examples/x.asp.styled
Normal file
12
scintilla/test/examples/x.asp.styled
Normal file
@ -0,0 +1,12 @@
|
||||
{15}<%@{16}language=javas{15}%>{0}
|
||||
{15}<%{56}
|
||||
#{61}include{56}
|
||||
{62}function{56} {61}x{65}(){56} {65}{{56}
|
||||
{65}}{56}
|
||||
{15}%>{0}
|
||||
{15}<%@{16}language=vbscript{15}%>{0}
|
||||
{15}<%{81}
|
||||
{84}sub{81} {86}x{81} {82}'comment {81}
|
||||
{15}%>{0}
|
||||
{1}<head>{0}
|
||||
{1}<body></body>{0}
|
7
scintilla/test/examples/x.cxx
Normal file
7
scintilla/test/examples/x.cxx
Normal file
@ -0,0 +1,7 @@
|
||||
// A demonstration program
|
||||
#include <stdio.h>
|
||||
int main() {
|
||||
double x[] = {3.14159,6.02e23,1.6e-19,1.0+1};
|
||||
int y[] = {75,0113,0x4b};
|
||||
printf("hello world %d\n", 9);
|
||||
}
|
7
scintilla/test/examples/x.cxx.styled
Normal file
7
scintilla/test/examples/x.cxx.styled
Normal file
@ -0,0 +1,7 @@
|
||||
{2}// A demonstration program
|
||||
{9}#include <stdio.h>
|
||||
{5}int{0} {11}main{10}(){0} {10}{{0}
|
||||
{11}double{0} {11}x{10}[]{0} {10}={0} {10}{{4}3.14159{10},{4}6.02e23{10},{4}1.6e-19{10},{4}1.0{10}+{4}1{10}};{0}
|
||||
{5}int{0} {11}y{10}[]{0} {10}={0} {10}{{4}75{10},{4}0113{10},{4}0x4b{10}};{0}
|
||||
{11}printf{10}({6}"hello world %d\n"{10},{0} {4}9{10});{0}
|
||||
{10}}{0}
|
47
scintilla/test/examples/x.d
Normal file
47
scintilla/test/examples/x.d
Normal file
@ -0,0 +1,47 @@
|
||||
$
|
||||
// /++ +/ doccomments are not yet supported
|
||||
/* */
|
||||
/** */
|
||||
/// drdr
|
||||
/+ /+ +/ +/
|
||||
//keyword test
|
||||
keyword1
|
||||
keyword2
|
||||
keyword4
|
||||
keyword5
|
||||
keyword6
|
||||
keyword7
|
||||
//unicode identifier test
|
||||
вапёasdÓΘΣαԷԸՑהכ拉麺とひシマイ단결을
|
||||
//strings test
|
||||
's
|
||||
'
|
||||
w's'w
|
||||
"multiline
|
||||
string"w
|
||||
e"zz"e
|
||||
r"asd\"e
|
||||
r"multiline
|
||||
string"c
|
||||
r`asd\`e
|
||||
`multiline
|
||||
string`d
|
||||
x"023 abc"e
|
||||
x"023
|
||||
abc"w
|
||||
//numbers test
|
||||
a[3..4]=3
|
||||
2.stringof
|
||||
2.0.stringof
|
||||
2.
|
||||
2.2e+2
|
||||
2.2e-2
|
||||
.2e+2
|
||||
.2
|
||||
2e+2
|
||||
0x2e+2
|
||||
0x2ep+10
|
||||
,.2.stringof,
|
||||
|
||||
end
|
||||
|
47
scintilla/test/examples/x.d.styled
Normal file
47
scintilla/test/examples/x.d.styled
Normal file
@ -0,0 +1,47 @@
|
||||
{14}${0}
|
||||
{2}// /++ +/ doccomments are not yet supported
|
||||
{1}/* */{0}
|
||||
{3}/** */{0}
|
||||
{15}/// drdr
|
||||
{4}/+ /+ +/ +/{0}
|
||||
{2}//keyword test
|
||||
{6}keyword1{0}
|
||||
{7}keyword2{0}
|
||||
{9}keyword4{0}
|
||||
{20}keyword5{0}
|
||||
{21}keyword6{0}
|
||||
{22}keyword7{0}
|
||||
{2}//unicode identifier test
|
||||
{14}вапёasdÓΘΣαԷԸՑהכ拉麺とひシマイ단결을{0}
|
||||
{2}//strings test
|
||||
{11}'s
|
||||
'
|
||||
{14}w{12}'s'{14}w{0}
|
||||
{10}"multiline
|
||||
string"w{0}
|
||||
{14}e{10}"zz"{14}e{0}
|
||||
{19}r"asd\"{14}e{0}
|
||||
{19}r"multiline
|
||||
string"c{0}
|
||||
{14}r{18}`asd\`{14}e{0}
|
||||
{18}`multiline
|
||||
string`d{0}
|
||||
{19}x"023 abc"{14}e{0}
|
||||
{19}x"023
|
||||
abc"w{0}
|
||||
{2}//numbers test
|
||||
{14}a{13}[{5}3{13}..{5}4{13}]={5}3{0}
|
||||
{5}2.stringof{0}
|
||||
{5}2.0{13}.{14}stringof{0}
|
||||
{5}2.{0}
|
||||
{5}2.2e+2{0}
|
||||
{5}2.2e-2{0}
|
||||
{5}.2e+2{0}
|
||||
{5}.2{0}
|
||||
{5}2e+2{0}
|
||||
{5}0x2e{13}+{5}2{0}
|
||||
{5}0x2ep+10{0}
|
||||
{13},{5}.2{13}.{14}stringof{13},{0}
|
||||
|
||||
{14}end{0}
|
||||
|
12
scintilla/test/examples/x.html
Normal file
12
scintilla/test/examples/x.html
Normal file
@ -0,0 +1,12 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<script type="text/javascript">
|
||||
var b = /abc/i.test('abc');
|
||||
'x\
|
||||
</t>'
|
||||
</script>
|
||||
<head>
|
||||
<meta name="Date.Modified" content="20010515" />
|
||||
<title>SinkWorld - Portability</title>
|
||||
<unknown>SinkWorld - Portability</unknown>
|
||||
<link rel="stylesheet" type="text/css" href="SW.css">
|
||||
</head>
|
12
scintilla/test/examples/x.html.styled
Normal file
12
scintilla/test/examples/x.html.styled
Normal file
@ -0,0 +1,12 @@
|
||||
{1}<html{8} {3}xmlns{8}={6}"http://www.w3.org/1999/xhtml"{1}>{0}
|
||||
{1}<script{8} {3}type{8}={6}"text/javascript"{1}>{40}
|
||||
{46}var{41} {46}b{41} {50}={41} {52}/abc/i{46}.test{50}({49}'abc'{50});{41}
|
||||
{49}'x\
|
||||
</t>'{41}
|
||||
{1}</script>{0}
|
||||
{1}<head>{0}
|
||||
{1}<meta{8} {3}name{8}={6}"Date.Modified"{8} {3}content{8}={6}"20010515"{8} {11}/>{0}
|
||||
{1}<title>{0}SinkWorld - Portability{1}</title>{0}
|
||||
{2}<unknown>{0}SinkWorld - Portability{2}</unknown>{0}
|
||||
{1}<link{8} {3}rel{8}={6}"stylesheet"{8} {3}type{8}={6}"text/css"{8} {3}href{8}={6}"SW.css"{1}>{0}
|
||||
{1}</head>{0}
|
6
scintilla/test/examples/x.php
Normal file
6
scintilla/test/examples/x.php
Normal file
@ -0,0 +1,6 @@
|
||||
<head> <!-- About to script -->
|
||||
<?php
|
||||
echo "<!-- -->\n";
|
||||
/* ?> */
|
||||
?>
|
||||
<strong>for</strong><b>if</b>
|
6
scintilla/test/examples/x.php.styled
Normal file
6
scintilla/test/examples/x.php.styled
Normal file
@ -0,0 +1,6 @@
|
||||
{1}<head>{0} {9}<!-- About to script -->{0}
|
||||
{18}<?php{118}
|
||||
echo {119}"<!-- -->\n"{127};{118}
|
||||
{124}/* ?> */{118}
|
||||
{18}?>{0}
|
||||
{1}<strong>{0}for{1}</strong><b>{0}if{1}</b>{0}
|
11
scintilla/test/examples/x.py
Normal file
11
scintilla/test/examples/x.py
Normal file
@ -0,0 +1,11 @@
|
||||
# Convert all punctuation characters except '_', '*', and '.' into spaces.
|
||||
def depunctuate(s):
|
||||
'''A docstring'''
|
||||
"""Docstring 2"""
|
||||
d = ""
|
||||
for ch in s:
|
||||
if ch in 'abcde':
|
||||
d = d + ch
|
||||
else:
|
||||
d = d + " "
|
||||
return d
|
11
scintilla/test/examples/x.py.styled
Normal file
11
scintilla/test/examples/x.py.styled
Normal file
@ -0,0 +1,11 @@
|
||||
{1}# Convert all punctuation characters except '_', '*', and '.' into spaces.{0}
|
||||
{5}def{0} {9}depunctuate{10}({11}s{10}):{0}
|
||||
{6}'''A docstring'''{0}
|
||||
{7}"""Docstring 2"""{0}
|
||||
{11}d{0} {10}={0} {3}""{0}
|
||||
{5}for{0} {11}ch{0} {5}in{0} {11}s{10}:{0}
|
||||
{5}if{0} {11}ch{0} {5}in{0} {4}'abcde'{10}:{0}
|
||||
{11}d{0} {10}={0} {11}d{0} {10}+{0} {11}ch{0}
|
||||
{5}else{10}:{0}
|
||||
{11}d{0} {10}={0} {11}d{0} {10}+{0} {3}" "{0}
|
||||
{5}return{0} {11}d{0}
|
9
scintilla/test/examples/x.vb
Normal file
9
scintilla/test/examples/x.vb
Normal file
@ -0,0 +1,9 @@
|
||||
' String"
|
||||
Dim a As String = "hello, world"
|
||||
Dim b As String = "hello world"
|
||||
Dim c As String = "Joe said ""Hello"" to me"
|
||||
Dim d As String = "\\\\server\\share\\file.txt"
|
||||
' Character
|
||||
""C "c"C "cc"C
|
||||
' Date
|
||||
d = #5/31/1993# or # 01/01/0001 12:00:00AM #
|
9
scintilla/test/examples/x.vb.styled
Normal file
9
scintilla/test/examples/x.vb.styled
Normal file
@ -0,0 +1,9 @@
|
||||
{1}' String"
|
||||
{3}Dim{0} {7}a{0} {3}As{0} {3}String{0} {6}={0} {4}"hello, world"{0}
|
||||
{3}Dim{0} {7}b{0} {3}As{0} {3}String{0} {6}={0} {4}"hello world"{0}
|
||||
{3}Dim{0} {7}c{0} {3}As{0} {3}String{0} {6}={0} {4}"Joe said ""Hello"" to me"{0}
|
||||
{3}Dim{0} {7}d{0} {3}As{0} {3}String{0} {6}={0} {4}"\\\\server\\share\\file.txt"{0}
|
||||
{1}' Character
|
||||
{4}""C{0} {4}"c"C{0} {4}"cc"C{0}
|
||||
{1}' Date
|
||||
{7}d{0} {6}={0} {8}#5/31/1993#{0} {3}or{0} {8}# 01/01/0001 12:00:00AM #{0}
|
113
scintilla/test/lexTests.py
Normal file
113
scintilla/test/lexTests.py
Normal file
@ -0,0 +1,113 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
import io
|
||||
import os
|
||||
import unittest
|
||||
|
||||
import XiteWin
|
||||
|
||||
keywordsHTML = [
|
||||
b"b body content head href html link meta "
|
||||
b"name rel script strong title type xmlns",
|
||||
b"function",
|
||||
b"sub"
|
||||
]
|
||||
|
||||
class TestLexers(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.xite = XiteWin.xiteFrame
|
||||
self.ed = self.xite.ed
|
||||
self.ed.ClearAll()
|
||||
self.ed.EmptyUndoBuffer()
|
||||
|
||||
def AsStyled(self):
|
||||
text = self.ed.Contents()
|
||||
data = io.BytesIO()
|
||||
prevStyle = -1
|
||||
for o in range(self.ed.Length):
|
||||
styleNow = self.ed.GetStyleAt(o)
|
||||
if styleNow != prevStyle:
|
||||
styleBuf = "{%0d}" % styleNow
|
||||
data.write(styleBuf.encode('utf-8'))
|
||||
prevStyle = styleNow
|
||||
data.write(text[o:o+1])
|
||||
return data.getvalue()
|
||||
|
||||
def LexExample(self, name, lexerName, keywords=None):
|
||||
if keywords is None:
|
||||
keywords = []
|
||||
self.ed.LexerLanguage = lexerName
|
||||
bits = self.ed.StyleBitsNeeded
|
||||
mask = 2 << bits - 1
|
||||
self.ed.StyleBits = bits
|
||||
for i in range(len(keywords)):
|
||||
self.ed.SetKeyWords(i, keywords[i])
|
||||
|
||||
nameExample = os.path.join("examples", name)
|
||||
namePrevious = nameExample +".styled"
|
||||
nameNew = nameExample +".new"
|
||||
with open(nameExample, "rb") as f:
|
||||
prog = f.read()
|
||||
BOM = b"\xEF\xBB\xBF"
|
||||
if prog.startswith(BOM):
|
||||
prog = prog[len(BOM):]
|
||||
lenDocument = len(prog)
|
||||
self.ed.AddText(lenDocument, prog)
|
||||
self.ed.Colourise(0, lenDocument)
|
||||
self.assertEquals(self.ed.EndStyled, lenDocument)
|
||||
with open(namePrevious, "rb") as f:
|
||||
prevStyled = f.read()
|
||||
progStyled = self.AsStyled()
|
||||
if progStyled != prevStyled:
|
||||
with open(nameNew, "wb") as f:
|
||||
f.write(progStyled)
|
||||
print(progStyled)
|
||||
print(prevStyled)
|
||||
self.assertEquals(progStyled, prevStyled)
|
||||
# The whole file doesn't parse like it did before so don't try line by line
|
||||
# as that is likely to fail many times.
|
||||
return
|
||||
|
||||
# Try partial lexes from the start of every line which should all be identical.
|
||||
for line in range(self.ed.LineCount):
|
||||
lineStart = self.ed.PositionFromLine(line)
|
||||
self.ed.StartStyling(lineStart, mask)
|
||||
self.assertEquals(self.ed.EndStyled, lineStart)
|
||||
self.ed.Colourise(0, lenDocument)
|
||||
progStyled = self.AsStyled()
|
||||
if progStyled != prevStyled:
|
||||
with open(nameNew, "wb") as f:
|
||||
f.write(progStyled)
|
||||
assertEquals(progStyled, prevStyled)
|
||||
# Give up after one failure
|
||||
return
|
||||
|
||||
def testCXX(self):
|
||||
self.LexExample("x.cxx", b"cpp", [b"int"])
|
||||
|
||||
def testPython(self):
|
||||
self.LexExample("x.py", b"python",
|
||||
[b"class def else for if import in print return while"])
|
||||
|
||||
def testHTML(self):
|
||||
self.LexExample("x.html", b"hypertext", keywordsHTML)
|
||||
|
||||
def testASP(self):
|
||||
self.LexExample("x.asp", b"hypertext", keywordsHTML)
|
||||
|
||||
def testPHP(self):
|
||||
self.LexExample("x.php", b"hypertext", keywordsHTML)
|
||||
|
||||
def testVB(self):
|
||||
self.LexExample("x.vb", b"vb", [b"as dim or string"])
|
||||
|
||||
def testD(self):
|
||||
self.LexExample("x.d", b"d",
|
||||
[b"keyword1", b"keyword2", b"", b"keyword4", b"keyword5",
|
||||
b"keyword6", b"keyword7"])
|
||||
|
||||
if __name__ == '__main__':
|
||||
XiteWin.main("lexTests")
|
83
scintilla/test/performanceTests.py
Normal file
83
scintilla/test/performanceTests.py
Normal file
@ -0,0 +1,83 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import with_statement
|
||||
|
||||
import os, string, time, unittest
|
||||
|
||||
import XiteWin
|
||||
|
||||
class TestPerformance(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.xite = XiteWin.xiteFrame
|
||||
self.ed = self.xite.ed
|
||||
self.ed.ClearAll()
|
||||
self.ed.EmptyUndoBuffer()
|
||||
|
||||
def testAddLine(self):
|
||||
data = (string.ascii_letters + string.digits + "\n").encode('utf-8')
|
||||
start = time.time()
|
||||
for i in range(1000):
|
||||
self.ed.AddText(len(data), data)
|
||||
self.assertEquals(self.ed.LineCount, i + 2)
|
||||
end = time.time()
|
||||
duration = end - start
|
||||
print("%6.3f testAddLine" % duration)
|
||||
self.xite.DoEvents()
|
||||
self.assert_(self.ed.Length > 0)
|
||||
|
||||
def testAddLineMiddle(self):
|
||||
data = (string.ascii_letters + string.digits + "\n").encode('utf-8')
|
||||
start = time.time()
|
||||
for i in range(1000):
|
||||
self.ed.AddText(len(data), data)
|
||||
self.assertEquals(self.ed.LineCount, i + 2)
|
||||
end = time.time()
|
||||
duration = end - start
|
||||
print("%6.3f testAddLineMiddle" % duration)
|
||||
self.xite.DoEvents()
|
||||
self.assert_(self.ed.Length > 0)
|
||||
|
||||
def testHuge(self):
|
||||
data = (string.ascii_letters + string.digits + "\n").encode('utf-8')
|
||||
data = data * 100000
|
||||
start = time.time()
|
||||
self.ed.AddText(len(data), data)
|
||||
end = time.time()
|
||||
duration = end - start
|
||||
print("%6.3f testHuge" % duration)
|
||||
self.xite.DoEvents()
|
||||
self.assert_(self.ed.Length > 0)
|
||||
|
||||
def testHugeInserts(self):
|
||||
data = (string.ascii_letters + string.digits + "\n").encode('utf-8')
|
||||
data = data * 100000
|
||||
insert = (string.digits + "\n").encode('utf-8')
|
||||
self.ed.AddText(len(data), data)
|
||||
start = time.time()
|
||||
for i in range(1000):
|
||||
self.ed.InsertText(0, insert)
|
||||
end = time.time()
|
||||
duration = end - start
|
||||
print("%6.3f testHugeInserts" % duration)
|
||||
self.xite.DoEvents()
|
||||
self.assert_(self.ed.Length > 0)
|
||||
|
||||
def testHugeReplace(self):
|
||||
oneLine = (string.ascii_letters + string.digits + "\n").encode('utf-8')
|
||||
data = oneLine * 100000
|
||||
insert = (string.digits + "\n").encode('utf-8')
|
||||
self.ed.AddText(len(data), data)
|
||||
start = time.time()
|
||||
for i in range(1000):
|
||||
self.ed.TargetStart = i * len(insert)
|
||||
self.ed.TargetEnd = self.ed.TargetStart + len(oneLine)
|
||||
self.ed.ReplaceTarget(len(insert), insert)
|
||||
end = time.time()
|
||||
duration = end - start
|
||||
print("%6.3f testHugeReplace" % duration)
|
||||
self.xite.DoEvents()
|
||||
self.assert_(self.ed.Length > 0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
XiteWin.main("performanceTests")
|
1874
scintilla/test/simpleTests.py
Normal file
1874
scintilla/test/simpleTests.py
Normal file
File diff suppressed because it is too large
Load Diff
14
scintilla/test/unit/README
Normal file
14
scintilla/test/unit/README
Normal file
@ -0,0 +1,14 @@
|
||||
The test/unit directory contains unit tests for Scintilla data structures.
|
||||
|
||||
The tests can be run on Windows or Linux using g++ and GNU make.
|
||||
The Google Test framework is used.
|
||||
http://code.google.com/p/googletest/
|
||||
|
||||
Google test must be installed first.
|
||||
On Linux, install the google test packages libgtest-dev and libgtest0.
|
||||
On Windows download Google test and install it as a peer to the directory
|
||||
containing scintilla. The makefile assumes it is in ../../../../gtest-1.5.0.
|
||||
|
||||
To run the tests:
|
||||
make
|
||||
./unitTest
|
4
scintilla/test/unit/SciTE.properties
Normal file
4
scintilla/test/unit/SciTE.properties
Normal file
@ -0,0 +1,4 @@
|
||||
if PLAT_WIN
|
||||
make.command=mingw32-make
|
||||
command.go.*.cxx=./unitTest
|
||||
command.go.needs.$(file.patterns.cplusplus)=$(make.command)
|
68
scintilla/test/unit/makefile
Normal file
68
scintilla/test/unit/makefile
Normal file
@ -0,0 +1,68 @@
|
||||
# Build all the unit tests
|
||||
# Should be run using mingw32-make on Windows
|
||||
|
||||
.SUFFIXES: .cxx
|
||||
|
||||
GTEST_DIR = ../../../../gtest-1.5.0
|
||||
|
||||
ifdef windir
|
||||
|
||||
DEL = del /q
|
||||
# Find Google Test headers.
|
||||
CPPFLAGS += -I$(GTEST_DIR)/include
|
||||
GTEST_ALL = gtest-all.o
|
||||
LINKFLAGS = $(CPPFLAGS) $(CXXFLAGS)
|
||||
EXE = unitTest.exe
|
||||
|
||||
else
|
||||
|
||||
DEL = rm -f
|
||||
CPPFLAGS = $(shell gtest-config --cppflags)
|
||||
CXXFLAGS = $(shell gtest-config --cxxflags)
|
||||
LINKFLAGS = $(shell gtest-config --ldflags --libs)
|
||||
EXE = unitTest
|
||||
|
||||
# For coverage testing with gcov
|
||||
#CPPFLAGS += -fprofile-arcs -ftest-coverage
|
||||
#LINKFLAGS += -fprofile-arcs -ftest-coverage
|
||||
|
||||
endif
|
||||
|
||||
#vpath %.cxx ../src ../lexlib ../lexers
|
||||
vpath %.cxx ../../src
|
||||
|
||||
|
||||
INCLUDEDIRS = -I ../../include -I ../../src -I../../lexlib
|
||||
|
||||
|
||||
# Find headers of test code.
|
||||
CPPFLAGS += $(INCLUDEDIRS)
|
||||
|
||||
CXXFLAGS += -g -Wall -Wextra -Wno-unused-function
|
||||
#~ CXXFLAGS += -g -Wall
|
||||
|
||||
CASES:=$(addsuffix .o,$(basename $(notdir $(wildcard test*.cxx))))
|
||||
TESTEDOBJS=ContractionState.o RunStyles.o
|
||||
|
||||
TESTS=$(EXE)
|
||||
|
||||
GTEST_HEADERS=$(GTEST_DIR)/include/gtest/*.h $(GTEST_DIR)/include/gtest/internal/*.h
|
||||
|
||||
all: $(TESTS)
|
||||
|
||||
clean:
|
||||
$(DEL) $(TESTS) *.a *.o *.exe *.gcov *.gcda *.gcno
|
||||
|
||||
# Usually you shouldn't tweak such internal variables, indicated by a
|
||||
# trailing _.
|
||||
GTEST_SRCS_ = $(GTEST_DIR)/src/*.cc $(GTEST_DIR)/src/*.h $(GTEST_HEADERS)
|
||||
|
||||
gtest-all.o: $(GTEST_SRCS_)
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -I$(GTEST_DIR) -c \
|
||||
$(GTEST_DIR)/src/gtest-all.cc
|
||||
|
||||
.cxx.o:
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $<
|
||||
|
||||
$(EXE): $(CASES) $(TESTEDOBJS) unitTest.o $(GTEST_ALL)
|
||||
$(CXX) $(LINKFLAGS) $^ -o $@
|
146
scintilla/test/unit/testContractionState.cxx
Normal file
146
scintilla/test/unit/testContractionState.cxx
Normal file
@ -0,0 +1,146 @@
|
||||
// Unit Tests for Scintilla internal data structures
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
#include "SplitVector.h"
|
||||
#include "Partitioning.h"
|
||||
#include "RunStyles.h"
|
||||
#include "ContractionState.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
// Test ContractionState.
|
||||
|
||||
class ContractionStateTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
pcs = new ContractionState();
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
delete pcs;
|
||||
pcs = 0;
|
||||
}
|
||||
|
||||
ContractionState *pcs;
|
||||
};
|
||||
|
||||
TEST_F(ContractionStateTest, IsEmptyInitially) {
|
||||
EXPECT_EQ(1, pcs->LinesInDoc());
|
||||
EXPECT_EQ(1, pcs->LinesDisplayed());
|
||||
EXPECT_EQ(0, pcs->DisplayFromDoc(0));
|
||||
EXPECT_EQ(0, pcs->DocFromDisplay(0));
|
||||
}
|
||||
|
||||
TEST_F(ContractionStateTest, OneLine) {
|
||||
pcs->InsertLine(0);
|
||||
EXPECT_EQ(2, pcs->LinesInDoc());
|
||||
EXPECT_EQ(2, pcs->LinesDisplayed());
|
||||
EXPECT_EQ(0, pcs->DisplayFromDoc(0));
|
||||
EXPECT_EQ(0, pcs->DocFromDisplay(0));
|
||||
EXPECT_EQ(1, pcs->DisplayFromDoc(1));
|
||||
EXPECT_EQ(1, pcs->DocFromDisplay(1));
|
||||
}
|
||||
|
||||
TEST_F(ContractionStateTest, InsertionThenDeletions) {
|
||||
pcs->InsertLines(0,4);
|
||||
pcs->DeleteLine(1);
|
||||
|
||||
EXPECT_EQ(4, pcs->LinesInDoc());
|
||||
EXPECT_EQ(4, pcs->LinesDisplayed());
|
||||
for (int l=0;l<4;l++) {
|
||||
EXPECT_EQ(l, pcs->DisplayFromDoc(l));
|
||||
EXPECT_EQ(l, pcs->DocFromDisplay(l));
|
||||
}
|
||||
|
||||
pcs->DeleteLines(0,2);
|
||||
EXPECT_EQ(2, pcs->LinesInDoc());
|
||||
EXPECT_EQ(2, pcs->LinesDisplayed());
|
||||
for (int l=0;l<2;l++) {
|
||||
EXPECT_EQ(l, pcs->DisplayFromDoc(l));
|
||||
EXPECT_EQ(l, pcs->DocFromDisplay(l));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ContractionStateTest, ShowHide) {
|
||||
pcs->InsertLines(0,4);
|
||||
EXPECT_EQ(true, pcs->GetVisible(0));
|
||||
EXPECT_EQ(true, pcs->GetVisible(1));
|
||||
EXPECT_EQ(true, pcs->GetVisible(2));
|
||||
EXPECT_EQ(5, pcs->LinesDisplayed());
|
||||
|
||||
pcs->SetVisible(1, 1, false);
|
||||
EXPECT_EQ(true, pcs->GetVisible(0));
|
||||
EXPECT_EQ(0, pcs->GetVisible(1));
|
||||
EXPECT_EQ(true, pcs->GetVisible(2));
|
||||
EXPECT_EQ(4, pcs->LinesDisplayed());
|
||||
EXPECT_EQ(1, pcs->HiddenLines());
|
||||
|
||||
pcs->SetVisible(1, 2, true);
|
||||
for (int l=0;l<4;l++) {
|
||||
EXPECT_EQ(true, pcs->GetVisible(0));
|
||||
}
|
||||
|
||||
pcs->SetVisible(1, 1, false);
|
||||
EXPECT_EQ(0, pcs->GetVisible(1));
|
||||
pcs->ShowAll();
|
||||
for (int l=0;l<4;l++) {
|
||||
EXPECT_EQ(true, pcs->GetVisible(0));
|
||||
}
|
||||
EXPECT_EQ(0, pcs->HiddenLines());
|
||||
}
|
||||
|
||||
TEST_F(ContractionStateTest, Hidden) {
|
||||
pcs->InsertLines(0,1);
|
||||
for (int l=0;l<2;l++) {
|
||||
EXPECT_EQ(true, pcs->GetVisible(0));
|
||||
}
|
||||
EXPECT_EQ(0, pcs->HiddenLines());
|
||||
|
||||
pcs->SetVisible(1, 1, false);
|
||||
EXPECT_EQ(true, pcs->GetVisible(0));
|
||||
EXPECT_EQ(0, pcs->GetVisible(1));
|
||||
EXPECT_EQ(1, pcs->HiddenLines());
|
||||
|
||||
pcs->SetVisible(1, 1, true);
|
||||
for (int l=0;l<2;l++) {
|
||||
EXPECT_EQ(true, pcs->GetVisible(0));
|
||||
}
|
||||
EXPECT_EQ(0, pcs->HiddenLines());
|
||||
}
|
||||
|
||||
TEST_F(ContractionStateTest, Contracting) {
|
||||
pcs->InsertLines(0,4);
|
||||
for (int l=0;l<4;l++) {
|
||||
EXPECT_EQ(true, pcs->GetExpanded(l));
|
||||
}
|
||||
|
||||
pcs->SetExpanded(2, false);
|
||||
EXPECT_EQ(true, pcs->GetExpanded(1));
|
||||
EXPECT_EQ(0, pcs->GetExpanded(2));
|
||||
EXPECT_EQ(true, pcs->GetExpanded(3));
|
||||
|
||||
EXPECT_EQ(2, pcs->ContractedNext(0));
|
||||
EXPECT_EQ(2, pcs->ContractedNext(1));
|
||||
EXPECT_EQ(2, pcs->ContractedNext(2));
|
||||
EXPECT_EQ(-1, pcs->ContractedNext(3));
|
||||
|
||||
pcs->SetExpanded(2, true);
|
||||
EXPECT_EQ(true, pcs->GetExpanded(1));
|
||||
EXPECT_EQ(true, pcs->GetExpanded(2));
|
||||
EXPECT_EQ(true, pcs->GetExpanded(3));
|
||||
}
|
||||
|
||||
TEST_F(ContractionStateTest, ChangeHeight) {
|
||||
pcs->InsertLines(0,4);
|
||||
for (int l=0;l<4;l++) {
|
||||
EXPECT_EQ(1, pcs->GetHeight(l));
|
||||
}
|
||||
|
||||
pcs->SetHeight(1, 2);
|
||||
EXPECT_EQ(1, pcs->GetHeight(0));
|
||||
EXPECT_EQ(2, pcs->GetHeight(1));
|
||||
EXPECT_EQ(1, pcs->GetHeight(2));
|
||||
}
|
208
scintilla/test/unit/testPartitioning.cxx
Normal file
208
scintilla/test/unit/testPartitioning.cxx
Normal file
@ -0,0 +1,208 @@
|
||||
// Unit Tests for Scintilla internal data structures
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
#include "SplitVector.h"
|
||||
#include "Partitioning.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
const int growSize = 4;
|
||||
|
||||
const int lengthTestArray = 8;
|
||||
static const int testArray[lengthTestArray] = {3, 4, 5, 6, 7, 8, 9, 10};
|
||||
|
||||
// Test SplitVectorWithRangeAdd.
|
||||
|
||||
class SplitVectorWithRangeAddTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
psvwra = new SplitVectorWithRangeAdd(growSize);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
delete psvwra;
|
||||
psvwra = 0;
|
||||
}
|
||||
|
||||
SplitVectorWithRangeAdd *psvwra;
|
||||
};
|
||||
|
||||
TEST_F(SplitVectorWithRangeAddTest, IsEmptyInitially) {
|
||||
EXPECT_EQ(0, psvwra->Length());
|
||||
}
|
||||
|
||||
TEST_F(SplitVectorWithRangeAddTest, IncrementExceptEnds) {
|
||||
psvwra->InsertFromArray(0, testArray, 0, lengthTestArray);
|
||||
psvwra->RangeAddDelta(1, lengthTestArray-1, 1);
|
||||
for (int i=0; i<psvwra->Length(); i++) {
|
||||
if ((i == 0) || (i == lengthTestArray-1))
|
||||
EXPECT_EQ(i+3, psvwra->ValueAt(i));
|
||||
else
|
||||
EXPECT_EQ(i+4, psvwra->ValueAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
// Test Partitioning.
|
||||
|
||||
class PartitioningTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
pp = new Partitioning(growSize);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
delete pp;
|
||||
pp = 0;
|
||||
}
|
||||
|
||||
Partitioning *pp;
|
||||
};
|
||||
|
||||
TEST_F(PartitioningTest, IsEmptyInitially) {
|
||||
EXPECT_EQ(1, pp->Partitions());
|
||||
EXPECT_EQ(0, pp->PositionFromPartition(pp->Partitions()));
|
||||
EXPECT_EQ(0, pp->PartitionFromPosition(0));
|
||||
}
|
||||
|
||||
TEST_F(PartitioningTest, SimpleInsert) {
|
||||
pp->InsertText(0, 1);
|
||||
EXPECT_EQ(1, pp->Partitions());
|
||||
EXPECT_EQ(1, pp->PositionFromPartition(pp->Partitions()));
|
||||
}
|
||||
|
||||
TEST_F(PartitioningTest, TwoPartitions) {
|
||||
pp->InsertText(0, 2);
|
||||
pp->InsertPartition(1, 1);
|
||||
EXPECT_EQ(2, pp->Partitions());
|
||||
EXPECT_EQ(0, pp->PositionFromPartition(0));
|
||||
EXPECT_EQ(1, pp->PositionFromPartition(1));
|
||||
EXPECT_EQ(2, pp->PositionFromPartition(2));
|
||||
}
|
||||
|
||||
TEST_F(PartitioningTest, MoveStart) {
|
||||
pp->InsertText(0, 3);
|
||||
pp->InsertPartition(1, 2);
|
||||
pp->SetPartitionStartPosition(1,1);
|
||||
EXPECT_EQ(2, pp->Partitions());
|
||||
EXPECT_EQ(0, pp->PositionFromPartition(0));
|
||||
EXPECT_EQ(1, pp->PositionFromPartition(1));
|
||||
EXPECT_EQ(3, pp->PositionFromPartition(2));
|
||||
}
|
||||
|
||||
TEST_F(PartitioningTest, InsertAgain) {
|
||||
pp->InsertText(0, 3);
|
||||
pp->InsertPartition(1, 2);
|
||||
pp->InsertText(0,3);
|
||||
pp->InsertText(1,2);
|
||||
EXPECT_EQ(2, pp->Partitions());
|
||||
EXPECT_EQ(0, pp->PositionFromPartition(0));
|
||||
EXPECT_EQ(5, pp->PositionFromPartition(1));
|
||||
EXPECT_EQ(8, pp->PositionFromPartition(2));
|
||||
}
|
||||
|
||||
TEST_F(PartitioningTest, InsertReversed) {
|
||||
pp->InsertText(0, 3);
|
||||
pp->InsertPartition(1, 2);
|
||||
pp->InsertText(1,2);
|
||||
pp->InsertText(0,3);
|
||||
EXPECT_EQ(2, pp->Partitions());
|
||||
EXPECT_EQ(0, pp->PositionFromPartition(0));
|
||||
EXPECT_EQ(5, pp->PositionFromPartition(1));
|
||||
EXPECT_EQ(8, pp->PositionFromPartition(2));
|
||||
}
|
||||
|
||||
TEST_F(PartitioningTest, InverseSearch) {
|
||||
pp->InsertText(0, 3);
|
||||
pp->InsertPartition(1, 2);
|
||||
pp->SetPartitionStartPosition(1,1);
|
||||
|
||||
EXPECT_EQ(2, pp->Partitions());
|
||||
EXPECT_EQ(0, pp->PositionFromPartition(0));
|
||||
EXPECT_EQ(1, pp->PositionFromPartition(1));
|
||||
EXPECT_EQ(3, pp->PositionFromPartition(2));
|
||||
|
||||
EXPECT_EQ(0, pp->PartitionFromPosition(0));
|
||||
EXPECT_EQ(1, pp->PartitionFromPosition(1));
|
||||
EXPECT_EQ(1, pp->PartitionFromPosition(2));
|
||||
|
||||
EXPECT_EQ(1, pp->PartitionFromPosition(3));
|
||||
}
|
||||
|
||||
TEST_F(PartitioningTest, DeletePartition) {
|
||||
pp->InsertText(0, 2);
|
||||
pp->InsertPartition(1, 1);
|
||||
pp->RemovePartition(1);
|
||||
EXPECT_EQ(1, pp->Partitions());
|
||||
EXPECT_EQ(0, pp->PositionFromPartition(0));
|
||||
EXPECT_EQ(2, pp->PositionFromPartition(1));
|
||||
}
|
||||
|
||||
TEST_F(PartitioningTest, DeleteAll) {
|
||||
pp->InsertText(0, 3);
|
||||
pp->InsertPartition(1, 2);
|
||||
pp->SetPartitionStartPosition(1,1);
|
||||
pp->DeleteAll();
|
||||
// Back to initial state
|
||||
EXPECT_EQ(1, pp->Partitions());
|
||||
EXPECT_EQ(0, pp->PositionFromPartition(pp->Partitions()));
|
||||
}
|
||||
|
||||
TEST_F(PartitioningTest, TestBackwards) {
|
||||
pp->InsertText(0, 10);
|
||||
pp->InsertPartition(1, 3);
|
||||
pp->InsertPartition(2, 6);
|
||||
pp->InsertPartition(3, 9);
|
||||
pp->InsertText(2,4);
|
||||
pp->InsertText(1,2);
|
||||
pp->InsertText(0,3);
|
||||
EXPECT_EQ(4, pp->Partitions());
|
||||
EXPECT_EQ(0, pp->PositionFromPartition(0));
|
||||
EXPECT_EQ(6, pp->PositionFromPartition(1));
|
||||
EXPECT_EQ(11, pp->PositionFromPartition(2));
|
||||
EXPECT_EQ(18, pp->PositionFromPartition(3));
|
||||
EXPECT_EQ(19, pp->PositionFromPartition(4));
|
||||
}
|
||||
|
||||
TEST_F(PartitioningTest, TestMany) {
|
||||
// Provoke backstep call
|
||||
pp->InsertText(0, 42);
|
||||
for (int i=0; i<20; i++) {
|
||||
pp->InsertPartition(i+1, (i+1) * 2);
|
||||
}
|
||||
for (int i=20; i>0; i--) {
|
||||
pp->InsertText(i,2);
|
||||
}
|
||||
EXPECT_EQ(21, pp->Partitions());
|
||||
for (int i=1; i<20; i++) {
|
||||
EXPECT_EQ(i*4 - 2, pp->PositionFromPartition(i));
|
||||
EXPECT_EQ(i, pp->PartitionFromPosition(i*4 - 2));
|
||||
}
|
||||
pp->InsertText(19,2);
|
||||
EXPECT_EQ(3, pp->PartitionFromPosition(10));
|
||||
pp->InsertText(0,2);
|
||||
pp->InsertText(0,-2);
|
||||
pp->RemovePartition(1);
|
||||
EXPECT_EQ(0, pp->PositionFromPartition(0));
|
||||
EXPECT_EQ(6, pp->PositionFromPartition(1));
|
||||
EXPECT_EQ(10, pp->PositionFromPartition(2));
|
||||
pp->RemovePartition(10);
|
||||
EXPECT_EQ(46, pp->PositionFromPartition(10));
|
||||
EXPECT_EQ(10, pp->PartitionFromPosition(46));
|
||||
EXPECT_EQ(50, pp->PositionFromPartition(11));
|
||||
EXPECT_EQ(11, pp->PartitionFromPosition(50));
|
||||
}
|
||||
|
||||
#if !PLAT_WIN
|
||||
// Omit death tests on Windows where they trigger a system "unitTest.exe has stopped working" popup.
|
||||
TEST_F(PartitioningTest, OutOfRangeDeathTest) {
|
||||
::testing::FLAGS_gtest_death_test_style = "threadsafe";
|
||||
pp->InsertText(0, 2);
|
||||
pp->InsertPartition(1, 1);
|
||||
EXPECT_EQ(2, pp->Partitions());
|
||||
ASSERT_DEATH(pp->PositionFromPartition(-1), "Assertion");
|
||||
ASSERT_DEATH(pp->PositionFromPartition(3), "Assertion");
|
||||
}
|
||||
#endif
|
283
scintilla/test/unit/testRunStyles.cxx
Normal file
283
scintilla/test/unit/testRunStyles.cxx
Normal file
@ -0,0 +1,283 @@
|
||||
// Unit Tests for Scintilla internal data structures
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
#include "SplitVector.h"
|
||||
#include "Partitioning.h"
|
||||
#include "RunStyles.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
// Test RunStyles.
|
||||
|
||||
class RunStylesTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
prs = new RunStyles();
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
delete prs;
|
||||
prs = 0;
|
||||
}
|
||||
|
||||
RunStyles *prs;
|
||||
};
|
||||
|
||||
TEST_F(RunStylesTest, IsEmptyInitially) {
|
||||
EXPECT_EQ(0, prs->Length());
|
||||
EXPECT_EQ(1, prs->Runs());
|
||||
}
|
||||
|
||||
TEST_F(RunStylesTest, SimpleInsert) {
|
||||
prs->InsertSpace(0, 1);
|
||||
EXPECT_EQ(1, prs->Length());
|
||||
EXPECT_EQ(1, prs->Runs());
|
||||
EXPECT_EQ(0, prs->ValueAt(0));
|
||||
EXPECT_EQ(1, prs->FindNextChange(0, prs->Length()));
|
||||
EXPECT_EQ(2, prs->FindNextChange(1, prs->Length()));
|
||||
}
|
||||
|
||||
TEST_F(RunStylesTest, TwoRuns) {
|
||||
prs->InsertSpace(0, 2);
|
||||
EXPECT_EQ(2, prs->Length());
|
||||
EXPECT_EQ(1, prs->Runs());
|
||||
prs->SetValueAt(0, 2);
|
||||
EXPECT_EQ(2, prs->Runs());
|
||||
EXPECT_EQ(2, prs->ValueAt(0));
|
||||
EXPECT_EQ(0, prs->ValueAt(1));
|
||||
EXPECT_EQ(1, prs->FindNextChange(0, prs->Length()));
|
||||
EXPECT_EQ(2, prs->FindNextChange(1, prs->Length()));
|
||||
EXPECT_EQ(3, prs->FindNextChange(2, prs->Length()));
|
||||
}
|
||||
|
||||
TEST_F(RunStylesTest, LongerRuns) {
|
||||
prs->InsertSpace(0, 5);
|
||||
prs->SetValueAt(0, 3);
|
||||
prs->SetValueAt(1, 3);
|
||||
EXPECT_EQ(3, prs->ValueAt(0));
|
||||
EXPECT_EQ(3, prs->ValueAt(1));
|
||||
EXPECT_EQ(0, prs->ValueAt(2));
|
||||
EXPECT_EQ(2, prs->Runs());
|
||||
|
||||
EXPECT_EQ(0, prs->StartRun(0));
|
||||
EXPECT_EQ(2, prs->EndRun(0));
|
||||
|
||||
EXPECT_EQ(0, prs->StartRun(1));
|
||||
EXPECT_EQ(2, prs->EndRun(1));
|
||||
|
||||
EXPECT_EQ(2, prs->StartRun(2));
|
||||
EXPECT_EQ(5, prs->EndRun(2));
|
||||
|
||||
EXPECT_EQ(2, prs->StartRun(3));
|
||||
EXPECT_EQ(5, prs->EndRun(3));
|
||||
|
||||
EXPECT_EQ(2, prs->StartRun(4));
|
||||
EXPECT_EQ(5, prs->EndRun(4));
|
||||
|
||||
// At end
|
||||
EXPECT_EQ(2, prs->StartRun(5));
|
||||
EXPECT_EQ(5, prs->EndRun(5));
|
||||
|
||||
// After end is same as end
|
||||
EXPECT_EQ(2, prs->StartRun(6));
|
||||
EXPECT_EQ(5, prs->EndRun(6));
|
||||
|
||||
EXPECT_EQ(2, prs->FindNextChange(0, prs->Length()));
|
||||
EXPECT_EQ(5, prs->FindNextChange(2, prs->Length()));
|
||||
EXPECT_EQ(6, prs->FindNextChange(5, prs->Length()));
|
||||
}
|
||||
|
||||
TEST_F(RunStylesTest, FillRange) {
|
||||
prs->InsertSpace(0, 5);
|
||||
int startFill = 1;
|
||||
int lengthFill = 3;
|
||||
EXPECT_EQ(true, prs->FillRange(startFill, 99, lengthFill));
|
||||
EXPECT_EQ(1, startFill);
|
||||
EXPECT_EQ(3, lengthFill);
|
||||
|
||||
EXPECT_EQ(0, prs->ValueAt(0));
|
||||
EXPECT_EQ(99, prs->ValueAt(1));
|
||||
EXPECT_EQ(99, prs->ValueAt(2));
|
||||
EXPECT_EQ(99, prs->ValueAt(3));
|
||||
EXPECT_EQ(0, prs->ValueAt(4));
|
||||
|
||||
EXPECT_EQ(0, prs->StartRun(0));
|
||||
EXPECT_EQ(1, prs->EndRun(0));
|
||||
|
||||
EXPECT_EQ(1, prs->StartRun(1));
|
||||
EXPECT_EQ(4, prs->EndRun(1));
|
||||
}
|
||||
|
||||
TEST_F(RunStylesTest, FillRangeAlreadyFilled) {
|
||||
prs->InsertSpace(0, 5);
|
||||
int startFill = 1;
|
||||
int lengthFill = 3;
|
||||
EXPECT_EQ(true, prs->FillRange(startFill, 99, lengthFill));
|
||||
EXPECT_EQ(1, startFill);
|
||||
EXPECT_EQ(3, lengthFill);
|
||||
|
||||
int startFill2 = 2;
|
||||
int lengthFill2 = 1;
|
||||
// Compiler warnings if 'false' used instead of '0' as expected value:
|
||||
EXPECT_EQ(0, prs->FillRange(startFill2, 99, lengthFill2));
|
||||
EXPECT_EQ(2, startFill2);
|
||||
EXPECT_EQ(1, lengthFill2);
|
||||
EXPECT_EQ(0, prs->ValueAt(0));
|
||||
EXPECT_EQ(99, prs->ValueAt(1));
|
||||
EXPECT_EQ(99, prs->ValueAt(2));
|
||||
EXPECT_EQ(99, prs->ValueAt(3));
|
||||
EXPECT_EQ(0, prs->ValueAt(4));
|
||||
EXPECT_EQ(3, prs->Runs());
|
||||
}
|
||||
|
||||
TEST_F(RunStylesTest, FillRangeAlreadyPartFilled) {
|
||||
prs->InsertSpace(0, 5);
|
||||
int startFill = 1;
|
||||
int lengthFill = 2;
|
||||
EXPECT_EQ(true, prs->FillRange(startFill, 99, lengthFill));
|
||||
EXPECT_EQ(1, startFill);
|
||||
EXPECT_EQ(2, lengthFill);
|
||||
|
||||
int startFill2 = 2;
|
||||
int lengthFill2 = 2;
|
||||
EXPECT_EQ(true, prs->FillRange(startFill2, 99, lengthFill2));
|
||||
EXPECT_EQ(3, startFill2);
|
||||
EXPECT_EQ(1, lengthFill2);
|
||||
EXPECT_EQ(3, prs->Runs());
|
||||
}
|
||||
|
||||
TEST_F(RunStylesTest, DeleteRange) {
|
||||
prs->InsertSpace(0, 5);
|
||||
prs->SetValueAt(0, 3);
|
||||
EXPECT_EQ(2, prs->Runs());
|
||||
prs->SetValueAt(1, 3);
|
||||
EXPECT_EQ(2, prs->Runs());
|
||||
prs->DeleteRange(1, 1);
|
||||
EXPECT_EQ(4, prs->Length());
|
||||
EXPECT_EQ(2, prs->Runs());
|
||||
EXPECT_EQ(3, prs->ValueAt(0));
|
||||
EXPECT_EQ(0, prs->ValueAt(1));
|
||||
|
||||
EXPECT_EQ(0, prs->StartRun(0));
|
||||
EXPECT_EQ(1, prs->EndRun(0));
|
||||
|
||||
EXPECT_EQ(1, prs->StartRun(1));
|
||||
EXPECT_EQ(4, prs->EndRun(1));
|
||||
|
||||
EXPECT_EQ(1, prs->StartRun(2));
|
||||
EXPECT_EQ(4, prs->EndRun(2));
|
||||
}
|
||||
|
||||
TEST_F(RunStylesTest, Find) {
|
||||
prs->InsertSpace(0, 5);
|
||||
int startFill = 1;
|
||||
int lengthFill = 3;
|
||||
EXPECT_EQ(true, prs->FillRange(startFill, 99, lengthFill));
|
||||
EXPECT_EQ(1, startFill);
|
||||
EXPECT_EQ(3, lengthFill);
|
||||
|
||||
EXPECT_EQ(0, prs->Find(0,0));
|
||||
EXPECT_EQ(1, prs->Find(99,0));
|
||||
EXPECT_EQ(-1, prs->Find(3,0));
|
||||
|
||||
EXPECT_EQ(4, prs->Find(0,1));
|
||||
EXPECT_EQ(1, prs->Find(99,1));
|
||||
EXPECT_EQ(-1, prs->Find(3,1));
|
||||
|
||||
EXPECT_EQ(4, prs->Find(0,2));
|
||||
EXPECT_EQ(2, prs->Find(99,2));
|
||||
EXPECT_EQ(-1, prs->Find(3,2));
|
||||
|
||||
EXPECT_EQ(4, prs->Find(0,4));
|
||||
EXPECT_EQ(-1, prs->Find(99,4));
|
||||
EXPECT_EQ(-1, prs->Find(3,4));
|
||||
|
||||
EXPECT_EQ(-1, prs->Find(0,5));
|
||||
EXPECT_EQ(-1, prs->Find(99,5));
|
||||
EXPECT_EQ(-1, prs->Find(3,5));
|
||||
|
||||
EXPECT_EQ(-1, prs->Find(0,6));
|
||||
EXPECT_EQ(-1, prs->Find(99,6));
|
||||
EXPECT_EQ(-1, prs->Find(3,6));
|
||||
}
|
||||
|
||||
TEST_F(RunStylesTest, AllSame) {
|
||||
EXPECT_EQ(true, prs->AllSame());
|
||||
prs->InsertSpace(0, 5);
|
||||
EXPECT_EQ(true, prs->AllSame());
|
||||
EXPECT_EQ(0, prs->AllSameAs(88));
|
||||
EXPECT_EQ(true, prs->AllSameAs(0));
|
||||
int startFill = 1;
|
||||
int lengthFill = 3;
|
||||
EXPECT_EQ(true, prs->FillRange(startFill, 99, lengthFill));
|
||||
EXPECT_EQ(0, prs->AllSame());
|
||||
EXPECT_EQ(0, prs->AllSameAs(88));
|
||||
EXPECT_EQ(0, prs->AllSameAs(0));
|
||||
EXPECT_EQ(true, prs->FillRange(startFill, 0, lengthFill));
|
||||
EXPECT_EQ(true, prs->AllSame());
|
||||
EXPECT_EQ(0, prs->AllSameAs(88));
|
||||
EXPECT_EQ(true, prs->AllSameAs(0));
|
||||
}
|
||||
|
||||
TEST_F(RunStylesTest, FindWithReversion) {
|
||||
prs->InsertSpace(0, 5);
|
||||
EXPECT_EQ(1, prs->Runs());
|
||||
|
||||
int startFill = 1;
|
||||
int lengthFill = 1;
|
||||
EXPECT_EQ(true, prs->FillRange(startFill, 99, lengthFill));
|
||||
EXPECT_EQ(1, startFill);
|
||||
EXPECT_EQ(1, lengthFill);
|
||||
EXPECT_EQ(3, prs->Runs());
|
||||
|
||||
startFill = 2;
|
||||
lengthFill = 1;
|
||||
EXPECT_EQ(true, prs->FillRange(startFill, 99, lengthFill));
|
||||
EXPECT_EQ(2, startFill);
|
||||
EXPECT_EQ(1, lengthFill);
|
||||
EXPECT_EQ(3, prs->Runs());
|
||||
|
||||
startFill = 1;
|
||||
lengthFill = 1;
|
||||
EXPECT_EQ(true, prs->FillRange(startFill, 0, lengthFill));
|
||||
EXPECT_EQ(3, prs->Runs());
|
||||
EXPECT_EQ(1, lengthFill);
|
||||
|
||||
startFill = 2;
|
||||
lengthFill = 1;
|
||||
EXPECT_EQ(true, prs->FillRange(startFill, 0, lengthFill));
|
||||
EXPECT_EQ(1, prs->Runs());
|
||||
EXPECT_EQ(1, lengthFill);
|
||||
|
||||
EXPECT_EQ(-1, prs->Find(0,6));
|
||||
}
|
||||
|
||||
TEST_F(RunStylesTest, FinalRunInversion) {
|
||||
EXPECT_EQ(1, prs->Runs());
|
||||
prs->InsertSpace(0, 1);
|
||||
EXPECT_EQ(1, prs->Runs());
|
||||
prs->SetValueAt(0, 1);
|
||||
EXPECT_EQ(1, prs->Runs());
|
||||
prs->InsertSpace(1, 1);
|
||||
EXPECT_EQ(1, prs->Runs());
|
||||
prs->SetValueAt(1, 1);
|
||||
EXPECT_EQ(1, prs->Runs());
|
||||
prs->SetValueAt(1, 0);
|
||||
EXPECT_EQ(2, prs->Runs());
|
||||
prs->SetValueAt(1, 1);
|
||||
EXPECT_EQ(1, prs->Runs());
|
||||
}
|
||||
|
||||
TEST_F(RunStylesTest, DeleteAll) {
|
||||
prs->InsertSpace(0, 5);
|
||||
prs->SetValueAt(0, 3);
|
||||
prs->SetValueAt(1, 3);
|
||||
prs->DeleteAll();
|
||||
EXPECT_EQ(0, prs->Length());
|
||||
EXPECT_EQ(0, prs->ValueAt(0));
|
||||
EXPECT_EQ(1, prs->Runs());
|
||||
}
|
||||
|
244
scintilla/test/unit/testSparseState.cxx
Normal file
244
scintilla/test/unit/testSparseState.cxx
Normal file
@ -0,0 +1,244 @@
|
||||
// Unit Tests for Scintilla internal data structures
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
#include "SparseState.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
// Test SparseState.
|
||||
|
||||
class SparseStateTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
pss = new SparseState<int>();
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
delete pss;
|
||||
pss = 0;
|
||||
}
|
||||
|
||||
SparseState<int> *pss;
|
||||
};
|
||||
|
||||
TEST_F(SparseStateTest, IsEmptyInitially) {
|
||||
EXPECT_EQ(0u, pss->size());
|
||||
int val = pss->ValueAt(0);
|
||||
EXPECT_EQ(0, val);
|
||||
}
|
||||
|
||||
TEST_F(SparseStateTest, SimpleSetAndGet) {
|
||||
pss->Set(0, 22);
|
||||
pss->Set(1, 23);
|
||||
EXPECT_EQ(2u, pss->size());
|
||||
EXPECT_EQ(0, pss->ValueAt(-1));
|
||||
EXPECT_EQ(22, pss->ValueAt(0));
|
||||
EXPECT_EQ(23, pss->ValueAt(1));
|
||||
EXPECT_EQ(23, pss->ValueAt(2));
|
||||
}
|
||||
|
||||
TEST_F(SparseStateTest, RetrieveBetween) {
|
||||
pss->Set(0, 10);
|
||||
pss->Set(2, 12);
|
||||
EXPECT_EQ(2u, pss->size());
|
||||
EXPECT_EQ(0, pss->ValueAt(-1));
|
||||
EXPECT_EQ(10, pss->ValueAt(0));
|
||||
EXPECT_EQ(10, pss->ValueAt(1));
|
||||
EXPECT_EQ(12, pss->ValueAt(2));
|
||||
}
|
||||
|
||||
TEST_F(SparseStateTest, RetrieveBefore) {
|
||||
pss->Set(2, 12);
|
||||
EXPECT_EQ(1u, pss->size());
|
||||
EXPECT_EQ(0, pss->ValueAt(-1));
|
||||
EXPECT_EQ(0, pss->ValueAt(0));
|
||||
EXPECT_EQ(0, pss->ValueAt(1));
|
||||
EXPECT_EQ(12, pss->ValueAt(2));
|
||||
}
|
||||
|
||||
TEST_F(SparseStateTest, Delete) {
|
||||
pss->Set(0, 30);
|
||||
pss->Set(2, 32);
|
||||
pss->Delete(2);
|
||||
EXPECT_EQ(1u, pss->size());
|
||||
EXPECT_EQ(0, pss->ValueAt(-1));
|
||||
EXPECT_EQ(30, pss->ValueAt(0));
|
||||
EXPECT_EQ(30, pss->ValueAt(1));
|
||||
EXPECT_EQ(30, pss->ValueAt(2));
|
||||
}
|
||||
|
||||
TEST_F(SparseStateTest, DeleteBetweeen) {
|
||||
pss->Set(0, 30);
|
||||
pss->Set(2, 32);
|
||||
pss->Delete(1);
|
||||
EXPECT_EQ(1u, pss->size());
|
||||
EXPECT_EQ(0, pss->ValueAt(-1));
|
||||
EXPECT_EQ(30, pss->ValueAt(0));
|
||||
EXPECT_EQ(30, pss->ValueAt(1));
|
||||
EXPECT_EQ(30, pss->ValueAt(2));
|
||||
}
|
||||
|
||||
TEST_F(SparseStateTest, ReplaceLast) {
|
||||
pss->Set(0, 30);
|
||||
pss->Set(2, 31);
|
||||
pss->Set(2, 32);
|
||||
EXPECT_EQ(2u, pss->size());
|
||||
EXPECT_EQ(0, pss->ValueAt(-1));
|
||||
EXPECT_EQ(30, pss->ValueAt(0));
|
||||
EXPECT_EQ(30, pss->ValueAt(1));
|
||||
EXPECT_EQ(32, pss->ValueAt(2));
|
||||
EXPECT_EQ(32, pss->ValueAt(3));
|
||||
}
|
||||
|
||||
TEST_F(SparseStateTest, OnlyChangeAppended) {
|
||||
pss->Set(0, 30);
|
||||
pss->Set(2, 31);
|
||||
pss->Set(3, 31);
|
||||
EXPECT_EQ(2u, pss->size());
|
||||
}
|
||||
|
||||
TEST_F(SparseStateTest, MergeBetween) {
|
||||
pss->Set(0, 30);
|
||||
pss->Set(2, 32);
|
||||
pss->Set(4, 34);
|
||||
EXPECT_EQ(3u, pss->size());
|
||||
|
||||
SparseState<int> ssAdditions(3);
|
||||
ssAdditions.Set(4, 34);
|
||||
EXPECT_EQ(1u, ssAdditions.size());
|
||||
bool mergeChanged = pss->Merge(ssAdditions,5);
|
||||
EXPECT_EQ(0, mergeChanged);
|
||||
|
||||
ssAdditions.Set(4, 44);
|
||||
EXPECT_EQ(1u, ssAdditions.size());
|
||||
mergeChanged = pss->Merge(ssAdditions,5);
|
||||
EXPECT_EQ(true, mergeChanged);
|
||||
EXPECT_EQ(3u, pss->size());
|
||||
EXPECT_EQ(44, pss->ValueAt(4));
|
||||
}
|
||||
|
||||
TEST_F(SparseStateTest, MergeAtExisting) {
|
||||
pss->Set(0, 30);
|
||||
pss->Set(2, 32);
|
||||
pss->Set(4, 34);
|
||||
EXPECT_EQ(3u, pss->size());
|
||||
|
||||
SparseState<int> ssAdditions(4);
|
||||
ssAdditions.Set(4, 34);
|
||||
EXPECT_EQ(1u, ssAdditions.size());
|
||||
bool mergeChanged = pss->Merge(ssAdditions,5);
|
||||
EXPECT_EQ(0, mergeChanged);
|
||||
|
||||
ssAdditions.Set(4, 44);
|
||||
EXPECT_EQ(1u, ssAdditions.size());
|
||||
mergeChanged = pss->Merge(ssAdditions,5);
|
||||
EXPECT_EQ(true, mergeChanged);
|
||||
EXPECT_EQ(3u, pss->size());
|
||||
EXPECT_EQ(44, pss->ValueAt(4));
|
||||
}
|
||||
|
||||
TEST_F(SparseStateTest, MergeWhichRemoves) {
|
||||
pss->Set(0, 30);
|
||||
pss->Set(2, 32);
|
||||
pss->Set(4, 34);
|
||||
EXPECT_EQ(3u, pss->size());
|
||||
|
||||
SparseState<int> ssAdditions(2);
|
||||
ssAdditions.Set(2, 22);
|
||||
EXPECT_EQ(1u, ssAdditions.size());
|
||||
EXPECT_EQ(22, ssAdditions.ValueAt(2));
|
||||
bool mergeChanged = pss->Merge(ssAdditions,5);
|
||||
EXPECT_EQ(true, mergeChanged);
|
||||
EXPECT_EQ(2u, pss->size());
|
||||
EXPECT_EQ(22, pss->ValueAt(2));
|
||||
}
|
||||
|
||||
TEST_F(SparseStateTest, MergeIgnoreSome) {
|
||||
pss->Set(0, 30);
|
||||
pss->Set(2, 32);
|
||||
pss->Set(4, 34);
|
||||
|
||||
SparseState<int> ssAdditions(2);
|
||||
ssAdditions.Set(2, 32);
|
||||
bool mergeChanged = pss->Merge(ssAdditions,3);
|
||||
|
||||
EXPECT_EQ(0, mergeChanged);
|
||||
EXPECT_EQ(2u, pss->size());
|
||||
EXPECT_EQ(32, pss->ValueAt(2));
|
||||
}
|
||||
|
||||
TEST_F(SparseStateTest, MergeIgnoreSomeStart) {
|
||||
pss->Set(0, 30);
|
||||
pss->Set(2, 32);
|
||||
pss->Set(4, 34);
|
||||
|
||||
SparseState<int> ssAdditions(2);
|
||||
ssAdditions.Set(2, 32);
|
||||
bool mergeChanged = pss->Merge(ssAdditions,2);
|
||||
|
||||
EXPECT_EQ(0, mergeChanged);
|
||||
EXPECT_EQ(2u, pss->size());
|
||||
EXPECT_EQ(32, pss->ValueAt(2));
|
||||
}
|
||||
|
||||
TEST_F(SparseStateTest, MergeIgnoreRepeat) {
|
||||
pss->Set(0, 30);
|
||||
pss->Set(2, 32);
|
||||
pss->Set(4, 34);
|
||||
|
||||
SparseState<int> ssAdditions(5);
|
||||
// Appending same value as at end of pss.
|
||||
ssAdditions.Set(5, 34);
|
||||
bool mergeChanged = pss->Merge(ssAdditions,6);
|
||||
|
||||
EXPECT_EQ(0, mergeChanged);
|
||||
EXPECT_EQ(3u, pss->size());
|
||||
EXPECT_EQ(34, pss->ValueAt(4));
|
||||
}
|
||||
|
||||
class SparseStateStringTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
pss = new SparseState<std::string>();
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
delete pss;
|
||||
pss = 0;
|
||||
}
|
||||
|
||||
SparseState<std::string> *pss;
|
||||
};
|
||||
|
||||
TEST_F(SparseStateStringTest, IsEmptyInitially) {
|
||||
EXPECT_EQ(0u, pss->size());
|
||||
std::string val = pss->ValueAt(0);
|
||||
EXPECT_EQ("", val);
|
||||
}
|
||||
|
||||
TEST_F(SparseStateStringTest, SimpleSetAndGet) {
|
||||
EXPECT_EQ(0u, pss->size());
|
||||
pss->Set(0, "22");
|
||||
pss->Set(1, "23");
|
||||
EXPECT_EQ(2u, pss->size());
|
||||
EXPECT_EQ("", pss->ValueAt(-1));
|
||||
EXPECT_EQ("22", pss->ValueAt(0));
|
||||
EXPECT_EQ("23", pss->ValueAt(1));
|
||||
EXPECT_EQ("23", pss->ValueAt(2));
|
||||
}
|
||||
|
||||
TEST_F(SparseStateStringTest, DeleteBetweeen) {
|
||||
pss->Set(0, "30");
|
||||
pss->Set(2, "32");
|
||||
pss->Delete(1);
|
||||
EXPECT_EQ(1u, pss->size());
|
||||
EXPECT_EQ("", pss->ValueAt(-1));
|
||||
EXPECT_EQ("30", pss->ValueAt(0));
|
||||
EXPECT_EQ("30", pss->ValueAt(1));
|
||||
EXPECT_EQ("30", pss->ValueAt(2));
|
||||
}
|
220
scintilla/test/unit/testSplitVector.cxx
Normal file
220
scintilla/test/unit/testSplitVector.cxx
Normal file
@ -0,0 +1,220 @@
|
||||
// Unit Tests for Scintilla internal data structures
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
#include "SplitVector.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
// Test SplitVector.
|
||||
|
||||
class SplitVectorTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
psv = new SplitVector<int>;
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
delete psv;
|
||||
psv = 0;
|
||||
}
|
||||
|
||||
SplitVector<int> *psv;
|
||||
};
|
||||
|
||||
const int lengthTestArray = 4;
|
||||
static const int testArray[4] = {3, 4, 5, 6};
|
||||
|
||||
TEST_F(SplitVectorTest, IsEmptyInitially) {
|
||||
EXPECT_EQ(0, psv->Length());
|
||||
}
|
||||
|
||||
TEST_F(SplitVectorTest, InsertOne) {
|
||||
psv->InsertValue(0, 10, 0);
|
||||
psv->Insert(5, 3);
|
||||
EXPECT_EQ(11, psv->Length());
|
||||
for (int i=0; i<psv->Length(); i++) {
|
||||
EXPECT_EQ((i == 5) ? 3 : 0, psv->ValueAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SplitVectorTest, Insertion) {
|
||||
psv->InsertValue(0, 10, 0);
|
||||
EXPECT_EQ(10, psv->Length());
|
||||
for (int i=0; i<psv->Length(); i++) {
|
||||
EXPECT_EQ(0, psv->ValueAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SplitVectorTest, EnsureLength) {
|
||||
psv->EnsureLength(4);
|
||||
EXPECT_EQ(4, psv->Length());
|
||||
for (int i=0; i<psv->Length(); i++) {
|
||||
EXPECT_EQ(0, psv->ValueAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SplitVectorTest, InsertFromArray) {
|
||||
psv->InsertFromArray(0, testArray, 0, lengthTestArray);
|
||||
EXPECT_EQ(lengthTestArray, psv->Length());
|
||||
for (int i=0; i<psv->Length(); i++) {
|
||||
EXPECT_EQ(i+3, psv->ValueAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SplitVectorTest, SetValue) {
|
||||
psv->InsertValue(0, 10, 0);
|
||||
psv->SetValueAt(5, 3);
|
||||
EXPECT_EQ(10, psv->Length());
|
||||
for (int i=0; i<psv->Length(); i++) {
|
||||
EXPECT_EQ((i == 5) ? 3 : 0, psv->ValueAt(i));
|
||||
}
|
||||
// Move the gap
|
||||
psv->InsertValue(7, 1, 17);
|
||||
EXPECT_EQ(17, psv->ValueAt(7));
|
||||
EXPECT_EQ(0, psv->ValueAt(8));
|
||||
// Set after the gap
|
||||
psv->SetValueAt(8, 19);
|
||||
EXPECT_EQ(19, psv->ValueAt(8));
|
||||
}
|
||||
|
||||
TEST_F(SplitVectorTest, Indexing) {
|
||||
psv->InsertValue(0, 10, 0);
|
||||
psv->SetValueAt(5, 3);
|
||||
EXPECT_EQ(10, psv->Length());
|
||||
for (int i=0; i<psv->Length(); i++) {
|
||||
EXPECT_EQ((i == 5) ? 3 : 0, (*psv)[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SplitVectorTest, Fill) {
|
||||
psv->InsertValue(0, 10, 0);
|
||||
EXPECT_EQ(10, psv->Length());
|
||||
psv->InsertValue(7, 1, 1);
|
||||
EXPECT_EQ(11, psv->Length());
|
||||
for (int i=0; i<psv->Length(); i++) {
|
||||
EXPECT_EQ((i == 7) ? 1 : 0, psv->ValueAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SplitVectorTest, DeleteOne) {
|
||||
psv->InsertFromArray(0, testArray, 0, lengthTestArray);
|
||||
psv->Delete(2);
|
||||
EXPECT_EQ(lengthTestArray-1, psv->Length());
|
||||
EXPECT_EQ(3, (*psv)[0]);
|
||||
EXPECT_EQ(4, (*psv)[1]);
|
||||
EXPECT_EQ(6, (*psv)[2]);
|
||||
}
|
||||
|
||||
TEST_F(SplitVectorTest, DeleteRange) {
|
||||
psv->InsertValue(0, 10, 0);
|
||||
EXPECT_EQ(10, psv->Length());
|
||||
psv->InsertValue(7, 1, 1);
|
||||
EXPECT_EQ(11, psv->Length());
|
||||
psv->DeleteRange(2, 3);
|
||||
EXPECT_EQ(8, psv->Length());
|
||||
for (int i=0; i<psv->Length(); i++) {
|
||||
EXPECT_EQ((i == 4) ? 1 : 0, psv->ValueAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SplitVectorTest, DeleteAll) {
|
||||
psv->InsertValue(0, 10, 0);
|
||||
psv->InsertValue(7, 1, 1);
|
||||
psv->DeleteRange(2, 3);
|
||||
psv->DeleteAll();
|
||||
EXPECT_EQ(0, psv->Length());
|
||||
}
|
||||
|
||||
TEST_F(SplitVectorTest, GetRange) {
|
||||
psv->InsertValue(0, 10, 0);
|
||||
psv->InsertValue(7, 1, 1);
|
||||
int retrieveArray[11] = {0};
|
||||
psv->GetRange(retrieveArray, 0, 11);
|
||||
for (int i=0; i<psv->Length(); i++) {
|
||||
EXPECT_EQ((i==7) ? 1 : 0, retrieveArray[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SplitVectorTest, GetRangeOverGap) {
|
||||
psv->InsertFromArray(0, testArray, 0, lengthTestArray);
|
||||
EXPECT_EQ(lengthTestArray, psv->Length());
|
||||
int retrieveArray[lengthTestArray] = {0};
|
||||
psv->GetRange(retrieveArray, 0, lengthTestArray);
|
||||
for (int i=0; i<psv->Length(); i++) {
|
||||
EXPECT_EQ(i+3, retrieveArray[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SplitVectorTest, ReplaceUp) {
|
||||
// Replace each element by inserting and then deleting the displaced element
|
||||
// This should perform many moves
|
||||
const int testLength=105;
|
||||
psv->EnsureLength(testLength);
|
||||
for (int i=0; i<testLength; i++)
|
||||
psv->SetValueAt(i, i+2);
|
||||
EXPECT_EQ(testLength, psv->Length());
|
||||
for (int i=0; i<psv->Length(); i++) {
|
||||
psv->InsertValue(i, 1, i+9);
|
||||
psv->Delete(i+1);
|
||||
}
|
||||
for (int i=0; i<psv->Length(); i++)
|
||||
EXPECT_EQ(i+9, psv->ValueAt(i));
|
||||
}
|
||||
|
||||
TEST_F(SplitVectorTest, ReplaceDown) {
|
||||
// From the end, replace each element by inserting and then deleting the displaced element
|
||||
// This should perform many moves
|
||||
const int testLength=24;
|
||||
psv->EnsureLength(testLength);
|
||||
for (int i=0; i<testLength; i++)
|
||||
psv->SetValueAt(i, i+12);
|
||||
EXPECT_EQ(testLength, psv->Length());
|
||||
for (int i=psv->Length()-1; i>=0; i--) {
|
||||
psv->InsertValue(i, 1, i+5);
|
||||
psv->Delete(i+1);
|
||||
}
|
||||
for (int i=0; i<psv->Length(); i++)
|
||||
EXPECT_EQ(i+5, psv->ValueAt(i));
|
||||
}
|
||||
|
||||
TEST_F(SplitVectorTest, BufferPointer) {
|
||||
psv->InsertFromArray(0, testArray, 0, lengthTestArray);
|
||||
int *retrievePointer = psv->BufferPointer();
|
||||
for (int i=0; i<psv->Length(); i++) {
|
||||
EXPECT_EQ(i+3, retrievePointer[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SplitVectorTest, DeleteBackAndForth) {
|
||||
psv->InsertValue(0, 10, 87);
|
||||
for (int i=0; i<10; i+=2) {
|
||||
int len = 10 - i;
|
||||
EXPECT_EQ(len, psv->Length());
|
||||
for (int i=0; i<psv->Length(); i++) {
|
||||
EXPECT_EQ(87, psv->ValueAt(i));
|
||||
}
|
||||
psv->Delete(len-1);
|
||||
psv->Delete(0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(SplitVectorTest, GrowSize) {
|
||||
psv->SetGrowSize(5);
|
||||
EXPECT_EQ(5, psv->GetGrowSize());
|
||||
}
|
||||
|
||||
TEST_F(SplitVectorTest, OutsideBounds) {
|
||||
psv->InsertValue(0, 10, 87);
|
||||
EXPECT_EQ(0, psv->ValueAt(-1));
|
||||
EXPECT_EQ(0, psv->ValueAt(10));
|
||||
|
||||
/* Could be a death test as this asserts:
|
||||
psv->SetValueAt(-1,98);
|
||||
psv->SetValueAt(10,99);
|
||||
EXPECT_EQ(0, psv->ValueAt(-1));
|
||||
EXPECT_EQ(0, psv->ValueAt(10));
|
||||
*/
|
||||
}
|
50
scintilla/test/unit/unitTest.cxx
Normal file
50
scintilla/test/unit/unitTest.cxx
Normal file
@ -0,0 +1,50 @@
|
||||
// Unit Tests for Scintilla internal data structures
|
||||
|
||||
/*
|
||||
Currently tested:
|
||||
SplitVector
|
||||
Partitioning
|
||||
RunStyles
|
||||
ContractionState
|
||||
|
||||
To do:
|
||||
Decoration
|
||||
DecorationList
|
||||
PerLine *
|
||||
CellBuffer *
|
||||
Range
|
||||
StyledText
|
||||
CaseFolder ...
|
||||
Document
|
||||
RESearch
|
||||
Selection
|
||||
UniConversion
|
||||
Style
|
||||
|
||||
lexlib:
|
||||
Accessor
|
||||
LexAccessor
|
||||
CharacterSet
|
||||
OptionSet
|
||||
PropSetSimple
|
||||
StyleContext
|
||||
WordList
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
// Needed for PLATFORM_ASSERT in code being tested
|
||||
|
||||
void Platform::Assert(const char *c, const char *file, int line) {
|
||||
fprintf(stderr, "Assertion [%s] failed at %s %d\n", c, file, line);
|
||||
abort();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
6
scintilla/test/xite.py
Normal file
6
scintilla/test/xite.py
Normal file
@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import XiteWin
|
||||
|
||||
if __name__ == "__main__":
|
||||
XiteWin.main("")
|
Loading…
Reference in New Issue
Block a user