From ffdebe56edada4b12d15a2d33f5deb1e8fb8d38f Mon Sep 17 00:00:00 2001 From: Don Ho Date: Fri, 25 Jan 2013 00:46:29 +0000 Subject: [PATCH] =?UTF-8?q?[BUG=5FFIXED]=20(Author:=20Fran=C3=A7ois-R=20Bo?= =?UTF-8?q?yer=20&=20Dave=20Brotherstone)=20Regex=20search=20and=20replace?= =?UTF-8?q?=20haning=20problem=20fix.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git-svn-id: svn://svn.tuxfamily.org/svnroot/notepadplus/repository/trunk@1009 f5eea248-9336-0410-98b8-ebc06183d4e3 --- .../src/ScitillaComponent/FindReplaceDlg.cpp | 231 +- .../src/ScitillaComponent/FindReplaceDlg.h | 18 +- scintilla/boostregex/BoostRegExSearch.cxx | 574 +++-- scintilla/include/BoostRegexSearch.h | 8 +- scintilla/include/Scintilla.iface | 7 + scintilla/test/MessageNumbers.py | 63 + scintilla/test/README | 31 + scintilla/test/XiteMenu.py | 26 + scintilla/test/XiteWin.py | 666 ++++++ scintilla/test/examples/x.asp | 12 + scintilla/test/examples/x.asp.styled | 12 + scintilla/test/examples/x.cxx | 7 + scintilla/test/examples/x.cxx.styled | 7 + scintilla/test/examples/x.d | 47 + scintilla/test/examples/x.d.styled | 47 + scintilla/test/examples/x.html | 12 + scintilla/test/examples/x.html.styled | 12 + scintilla/test/examples/x.php | 6 + scintilla/test/examples/x.php.styled | 6 + scintilla/test/examples/x.py | 11 + scintilla/test/examples/x.py.styled | 11 + scintilla/test/examples/x.vb | 9 + scintilla/test/examples/x.vb.styled | 9 + scintilla/test/lexTests.py | 113 + scintilla/test/performanceTests.py | 83 + scintilla/test/simpleTests.py | 1874 +++++++++++++++++ scintilla/test/unit/README | 14 + scintilla/test/unit/SciTE.properties | 4 + scintilla/test/unit/makefile | 68 + scintilla/test/unit/testContractionState.cxx | 146 ++ scintilla/test/unit/testPartitioning.cxx | 208 ++ scintilla/test/unit/testRunStyles.cxx | 283 +++ scintilla/test/unit/testSparseState.cxx | 244 +++ scintilla/test/unit/testSplitVector.cxx | 220 ++ scintilla/test/unit/unitTest.cxx | 50 + scintilla/test/xite.py | 6 + 36 files changed, 4854 insertions(+), 291 deletions(-) create mode 100644 scintilla/test/MessageNumbers.py create mode 100644 scintilla/test/README create mode 100644 scintilla/test/XiteMenu.py create mode 100644 scintilla/test/XiteWin.py create mode 100644 scintilla/test/examples/x.asp create mode 100644 scintilla/test/examples/x.asp.styled create mode 100644 scintilla/test/examples/x.cxx create mode 100644 scintilla/test/examples/x.cxx.styled create mode 100644 scintilla/test/examples/x.d create mode 100644 scintilla/test/examples/x.d.styled create mode 100644 scintilla/test/examples/x.html create mode 100644 scintilla/test/examples/x.html.styled create mode 100644 scintilla/test/examples/x.php create mode 100644 scintilla/test/examples/x.php.styled create mode 100644 scintilla/test/examples/x.py create mode 100644 scintilla/test/examples/x.py.styled create mode 100644 scintilla/test/examples/x.vb create mode 100644 scintilla/test/examples/x.vb.styled create mode 100644 scintilla/test/lexTests.py create mode 100644 scintilla/test/performanceTests.py create mode 100644 scintilla/test/simpleTests.py create mode 100644 scintilla/test/unit/README create mode 100644 scintilla/test/unit/SciTE.properties create mode 100644 scintilla/test/unit/makefile create mode 100644 scintilla/test/unit/testContractionState.cxx create mode 100644 scintilla/test/unit/testPartitioning.cxx create mode 100644 scintilla/test/unit/testRunStyles.cxx create mode 100644 scintilla/test/unit/testSparseState.cxx create mode 100644 scintilla/test/unit/testSplitVector.cxx create mode 100644 scintilla/test/unit/unitTest.cxx create mode 100644 scintilla/test/xite.py diff --git a/PowerEditor/src/ScitillaComponent/FindReplaceDlg.cpp b/PowerEditor/src/ScitillaComponent/FindReplaceDlg.cpp index f426f5b4..ec50803a 100644 --- a/PowerEditor/src/ScitillaComponent/FindReplaceDlg.cpp +++ b/PowerEditor/src/ScitillaComponent/FindReplaceDlg.cpp @@ -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("^ 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; diff --git a/PowerEditor/src/ScitillaComponent/FindReplaceDlg.h b/PowerEditor/src/ScitillaComponent/FindReplaceDlg.h index 9f091d87..90c7ddd7 100644 --- a/PowerEditor/src/ScitillaComponent/FindReplaceDlg.h +++ b/PowerEditor/src/ScitillaComponent/FindReplaceDlg.h @@ -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 diff --git a/scintilla/boostregex/BoostRegExSearch.cxx b/scintilla/boostregex/BoostRegExSearch.cxx index d37e7491..f8c8ce93 100644 --- a/scintilla/boostregex/BoostRegExSearch.cxx +++ b/scintilla/boostregex/BoostRegExSearch.cxx @@ -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 @@ -35,49 +35,196 @@ using namespace Scintilla; using namespace boost; - -typedef basic_regex charregex_t; -typedef boost::wregex wcharregex_t; -// , std::vector >::allocator_type -typedef match_results utf8match_t; -typedef match_results 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 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 Regex; + typedef match_results 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 _ansi; + EncodingDependent _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(compileFlags)); - delete [] wchars; - m_lastRegexUtf8string = s; - } - else - { // Ansi - m_charre = charregex_t(s, static_cast(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(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(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 +BoostRegexSearch::Match BoostRegexSearch::EncodingDependent::FindText(SearchParameters& search) +{ + compileRegex(search._regexString, search._compileFlags); + return (search._direction > 0) + ? FindTextForward(search) + : FindTextBackward(search); +} + +template +BoostRegexSearch::Match BoostRegexSearch::EncodingDependent::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 +BoostRegexSearch::Match BoostRegexSearch::EncodingDependent::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 +void BoostRegexSearch::EncodingDependent::compileRegex(const char *regex, const int compileFlags) +{ + if (_lastCompileFlags != compileFlags || _lastRegexString != regex) + { + _regex = Regex(CharTPtr(regex), static_cast(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 +char *BoostRegexSearch::EncodingDependent::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()); } \ No newline at end of file diff --git a/scintilla/include/BoostRegexSearch.h b/scintilla/include/BoostRegexSearch.h index 8c03a6b7..e532c356 100644 --- a/scintilla/include/BoostRegexSearch.h +++ b/scintilla/include/BoostRegexSearch.h @@ -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 \ No newline at end of file diff --git a/scintilla/include/Scintilla.iface b/scintilla/include/Scintilla.iface index ca115db1..3866f5a2 100644 --- a/scintilla/include/Scintilla.iface +++ b/scintilla/include/Scintilla.iface @@ -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) diff --git a/scintilla/test/MessageNumbers.py b/scintilla/test/MessageNumbers.py new file mode 100644 index 00000000..ef1c6fb5 --- /dev/null +++ b/scintilla/test/MessageNumbers.py @@ -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 + diff --git a/scintilla/test/README b/scintilla/test/README new file mode 100644 index 00000000..64787525 --- /dev/null +++ b/scintilla/test/README @@ -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 +. diff --git a/scintilla/test/XiteMenu.py b/scintilla/test/XiteMenu.py new file mode 100644 index 00000000..66fe77cd --- /dev/null +++ b/scintilla/test/XiteMenu.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- + +""" Define the menu structure used by the Pentacle applications """ + +MenuStructure = [ + ["&File", [ + ["&New", "N"], + ["&Open...", "O"], + ["&Save", "S"], + ["Save &As...", "S"], + ["Test", ""], + ["Exercised", ""], + ["Uncalled", ""], + ["-", ""], + ["&Exit", ""]]], + [ "&Edit", [ + ["&Undo", "Z"], + ["&Redo", "Y"], + ["-", ""], + ["Cu&t", "X"], + ["&Copy", "C"], + ["&Paste", "V"], + ["&Delete", "Del"], + ["Select &All", "A"], + ]], +] diff --git a/scintilla/test/XiteWin.py b/scintilla/test/XiteWin.py new file mode 100644 index 00000000..a4931613 --- /dev/null +++ b/scintilla/test/XiteWin.py @@ -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 += "" + if IsKeyDown(VK_SHIFT): + keyName += "" + 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, "") + 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("", "Ctrl+") + keyText = keyText.replace("", "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() diff --git a/scintilla/test/examples/x.asp b/scintilla/test/examples/x.asp new file mode 100644 index 00000000..ff7c66f2 --- /dev/null +++ b/scintilla/test/examples/x.asp @@ -0,0 +1,12 @@ +<%@language=javas%> +<% +#include +function x() { +} +%> +<%@language=vbscript%> +<% +sub x 'comment +%> + + diff --git a/scintilla/test/examples/x.asp.styled b/scintilla/test/examples/x.asp.styled new file mode 100644 index 00000000..135af73f --- /dev/null +++ b/scintilla/test/examples/x.asp.styled @@ -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}{0} +{1}{0} diff --git a/scintilla/test/examples/x.cxx b/scintilla/test/examples/x.cxx new file mode 100644 index 00000000..15fbc48b --- /dev/null +++ b/scintilla/test/examples/x.cxx @@ -0,0 +1,7 @@ +// A demonstration program +#include +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); +} diff --git a/scintilla/test/examples/x.cxx.styled b/scintilla/test/examples/x.cxx.styled new file mode 100644 index 00000000..07da1c64 --- /dev/null +++ b/scintilla/test/examples/x.cxx.styled @@ -0,0 +1,7 @@ +{2}// A demonstration program +{9}#include +{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} diff --git a/scintilla/test/examples/x.d b/scintilla/test/examples/x.d new file mode 100644 index 00000000..48f3ffe1 --- /dev/null +++ b/scintilla/test/examples/x.d @@ -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 + diff --git a/scintilla/test/examples/x.d.styled b/scintilla/test/examples/x.d.styled new file mode 100644 index 00000000..3a52dcd8 --- /dev/null +++ b/scintilla/test/examples/x.d.styled @@ -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} + diff --git a/scintilla/test/examples/x.html b/scintilla/test/examples/x.html new file mode 100644 index 00000000..7c5e1d25 --- /dev/null +++ b/scintilla/test/examples/x.html @@ -0,0 +1,12 @@ + + + + + SinkWorld - Portability + SinkWorld - Portability + + diff --git a/scintilla/test/examples/x.html.styled b/scintilla/test/examples/x.html.styled new file mode 100644 index 00000000..f90e8246 --- /dev/null +++ b/scintilla/test/examples/x.html.styled @@ -0,0 +1,12 @@ +{1}{0} +{1}{40} +{46}var{41} {46}b{41} {50}={41} {52}/abc/i{46}.test{50}({49}'abc'{50});{41} +{49}'x\ +'{41} +{1}{0} +{1}{0} + {1}{0} + {1}{0}SinkWorld - Portability{1}{0} + {2}{0}SinkWorld - Portability{2}{0} + {1}{0} +{1}{0} diff --git a/scintilla/test/examples/x.php b/scintilla/test/examples/x.php new file mode 100644 index 00000000..7c28d367 --- /dev/null +++ b/scintilla/test/examples/x.php @@ -0,0 +1,6 @@ + +\n"; +/* ?> */ +?> +forif diff --git a/scintilla/test/examples/x.php.styled b/scintilla/test/examples/x.php.styled new file mode 100644 index 00000000..944e3124 --- /dev/null +++ b/scintilla/test/examples/x.php.styled @@ -0,0 +1,6 @@ +{1}{0} {9}{0} +{18}\n"{127};{118} +{124}/* ?> */{118} +{18}?>{0} +{1}{0}for{1}{0}if{1}{0} diff --git a/scintilla/test/examples/x.py b/scintilla/test/examples/x.py new file mode 100644 index 00000000..7e577dd2 --- /dev/null +++ b/scintilla/test/examples/x.py @@ -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 diff --git a/scintilla/test/examples/x.py.styled b/scintilla/test/examples/x.py.styled new file mode 100644 index 00000000..983c3b83 --- /dev/null +++ b/scintilla/test/examples/x.py.styled @@ -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} diff --git a/scintilla/test/examples/x.vb b/scintilla/test/examples/x.vb new file mode 100644 index 00000000..35882211 --- /dev/null +++ b/scintilla/test/examples/x.vb @@ -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 # diff --git a/scintilla/test/examples/x.vb.styled b/scintilla/test/examples/x.vb.styled new file mode 100644 index 00000000..bc4e9e06 --- /dev/null +++ b/scintilla/test/examples/x.vb.styled @@ -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} diff --git a/scintilla/test/lexTests.py b/scintilla/test/lexTests.py new file mode 100644 index 00000000..4eb3cd98 --- /dev/null +++ b/scintilla/test/lexTests.py @@ -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") diff --git a/scintilla/test/performanceTests.py b/scintilla/test/performanceTests.py new file mode 100644 index 00000000..d4613b6f --- /dev/null +++ b/scintilla/test/performanceTests.py @@ -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") diff --git a/scintilla/test/simpleTests.py b/scintilla/test/simpleTests.py new file mode 100644 index 00000000..99c8268a --- /dev/null +++ b/scintilla/test/simpleTests.py @@ -0,0 +1,1874 @@ +# -*- coding: utf-8 -*- + +from __future__ import with_statement +from __future__ import unicode_literals + +import codecs, ctypes, os, sys, unittest + +import XiteWin + +class TestSimple(unittest.TestCase): + + def setUp(self): + self.xite = XiteWin.xiteFrame + self.ed = self.xite.ed + self.ed.ClearAll() + self.ed.EmptyUndoBuffer() + + def testLength(self): + self.assertEquals(self.ed.Length, 0) + + def testAddText(self): + self.ed.AddText(1, "x") + self.assertEquals(self.ed.Length, 1) + self.assertEquals(self.ed.GetCharAt(0), ord("x")) + self.assertEquals(self.ed.GetStyleAt(0), 0) + self.ed.ClearAll() + self.assertEquals(self.ed.Length, 0) + + # def testDeleteRange(self): + # self.ed.AddText(5, b"abcde") + # self.assertEquals(self.ed.Length, 5) + # self.ed.DeleteRange(1, 2) + # self.assertEquals(self.ed.Length, 3) + # self.assertEquals(self.ed.Contents(), b"ade") + + def testAddStyledText(self): + self.assertEquals(self.ed.EndStyled, 0) + self.ed.AddStyledText(2, b"x\002") + self.assertEquals(self.ed.Length, 1) + self.assertEquals(self.ed.GetCharAt(0), ord("x")) + self.assertEquals(self.ed.GetStyleAt(0), 2) + self.assertEquals(self.ed.StyledTextRange(0, 1), b"x\002") + self.ed.ClearDocumentStyle() + self.assertEquals(self.ed.Length, 1) + self.assertEquals(self.ed.GetCharAt(0), ord("x")) + self.assertEquals(self.ed.GetStyleAt(0), 0) + self.assertEquals(self.ed.StyledTextRange(0, 1), b"x\0") + + def testStyling(self): + self.assertEquals(self.ed.EndStyled, 0) + self.ed.AddStyledText(4, b"x\002y\003") + self.assertEquals(self.ed.StyledTextRange(0, 2), b"x\002y\003") + self.ed.StartStyling(0,0xf) + self.ed.SetStyling(1, 5) + self.assertEquals(self.ed.StyledTextRange(0, 2), b"x\005y\003") + # Set the mask so 0 bit changed but not 2 bit + self.ed.StartStyling(0,0x1) + self.ed.SetStyling(1, 0) + self.assertEquals(self.ed.StyledTextRange(0, 2), b"x\004y\003") + + self.ed.StartStyling(0,0xff) + self.ed.SetStylingEx(2, b"\100\101") + self.assertEquals(self.ed.StyledTextRange(0, 2), b"x\100y\101") + + def testPosition(self): + self.assertEquals(self.ed.CurrentPos, 0) + self.assertEquals(self.ed.Anchor, 0) + self.ed.AddText(1, "x") + # Caret has automatically moved + self.assertEquals(self.ed.CurrentPos, 1) + self.assertEquals(self.ed.Anchor, 1) + self.ed.SelectAll() + self.assertEquals(self.ed.CurrentPos, 0) + self.assertEquals(self.ed.Anchor, 1) + self.ed.Anchor = 0 + self.assertEquals(self.ed.Anchor, 0) + # Check line positions + self.assertEquals(self.ed.PositionFromLine(0), 0) + self.assertEquals(self.ed.GetLineEndPosition(0), 1) + self.assertEquals(self.ed.PositionFromLine(1), 1) + + self.ed.CurrentPos = 1 + self.assertEquals(self.ed.Anchor, 0) + self.assertEquals(self.ed.CurrentPos, 1) + + def testSelection(self): + self.assertEquals(self.ed.CurrentPos, 0) + self.assertEquals(self.ed.Anchor, 0) + self.assertEquals(self.ed.SelectionStart, 0) + self.assertEquals(self.ed.SelectionEnd, 0) + self.ed.AddText(1, "x") + self.ed.SelectionStart = 0 + self.assertEquals(self.ed.CurrentPos, 1) + self.assertEquals(self.ed.Anchor, 0) + self.assertEquals(self.ed.SelectionStart, 0) + self.assertEquals(self.ed.SelectionEnd, 1) + self.ed.SelectionStart = 1 + self.assertEquals(self.ed.CurrentPos, 1) + self.assertEquals(self.ed.Anchor, 1) + self.assertEquals(self.ed.SelectionStart, 1) + self.assertEquals(self.ed.SelectionEnd, 1) + + self.ed.SelectionEnd = 0 + self.assertEquals(self.ed.CurrentPos, 0) + self.assertEquals(self.ed.Anchor, 0) + + def testSetSelection(self): + self.ed.AddText(4, b"abcd") + self.ed.SetSel(1, 3) + self.assertEquals(self.ed.SelectionStart, 1) + self.assertEquals(self.ed.SelectionEnd, 3) + result = b"\0" * 5 + length = self.ed.GetSelText(0, result) + self.assertEquals(length, 3) + self.assertEquals(result[:length], b"bc\0") + self.ed.ReplaceSel(0, b"1234") + self.assertEquals(self.ed.Length, 6) + self.assertEquals(self.ed.Contents(), b"a1234d") + + def testReadOnly(self): + self.ed.AddText(1, b"x") + self.assertEquals(self.ed.ReadOnly, 0) + self.assertEquals(self.ed.Contents(), b"x") + self.ed.ReadOnly = 1 + self.assertEquals(self.ed.ReadOnly, 1) + self.ed.AddText(1, b"x") + self.assertEquals(self.ed.Contents(), b"x") + self.ed.ReadOnly = 0 + self.ed.AddText(1, b"x") + self.assertEquals(self.ed.Contents(), b"xx") + self.ed.Null() + self.assertEquals(self.ed.Contents(), b"xx") + + def testAddLine(self): + data = b"x" * 70 + b"\n" + for i in range(5): + self.ed.AddText(len(data), data) + self.xite.DoEvents() + self.assertEquals(self.ed.LineCount, i + 2) + self.assert_(self.ed.Length > 0) + + def testInsertText(self): + data = b"xy" + self.ed.InsertText(0, data) + self.assertEquals(self.ed.Length, 2) + self.assertEquals(data, self.ed.ByteRange(0,2)) + + self.ed.InsertText(1, data) + # Should now be "xxyy" + self.assertEquals(self.ed.Length, 4) + self.assertEquals(b"xxyy", self.ed.ByteRange(0,4)) + + def testInsertNul(self): + data = b"\0" + self.ed.AddText(1, data) + self.assertEquals(self.ed.Length, 1) + self.assertEquals(data, self.ed.ByteRange(0,1)) + + def testUndoRedo(self): + data = b"xy" + self.assertEquals(self.ed.Modify, 0) + self.assertEquals(self.ed.UndoCollection, 1) + self.assertEquals(self.ed.CanRedo(), 0) + self.assertEquals(self.ed.CanUndo(), 0) + self.ed.InsertText(0, data) + self.assertEquals(self.ed.Length, 2) + self.assertEquals(self.ed.Modify, 1) + self.assertEquals(self.ed.CanRedo(), 0) + self.assertEquals(self.ed.CanUndo(), 1) + self.ed.Undo() + self.assertEquals(self.ed.Length, 0) + self.assertEquals(self.ed.Modify, 0) + self.assertEquals(self.ed.CanRedo(), 1) + self.assertEquals(self.ed.CanUndo(), 0) + self.ed.Redo() + self.assertEquals(self.ed.Length, 2) + self.assertEquals(self.ed.Modify, 1) + self.assertEquals(data, self.ed.Contents()) + self.assertEquals(self.ed.CanRedo(), 0) + self.assertEquals(self.ed.CanUndo(), 1) + + def testUndoSavePoint(self): + data = b"xy" + self.assertEquals(self.ed.Modify, 0) + self.ed.InsertText(0, data) + self.assertEquals(self.ed.Modify, 1) + self.ed.SetSavePoint() + self.assertEquals(self.ed.Modify, 0) + self.ed.InsertText(0, data) + self.assertEquals(self.ed.Modify, 1) + + def testUndoCollection(self): + data = b"xy" + self.assertEquals(self.ed.UndoCollection, 1) + self.ed.UndoCollection = 0 + self.assertEquals(self.ed.UndoCollection, 0) + self.ed.InsertText(0, data) + self.assertEquals(self.ed.CanRedo(), 0) + self.assertEquals(self.ed.CanUndo(), 0) + self.ed.UndoCollection = 1 + + def testGetColumn(self): + self.ed.AddText(1, b"x") + self.assertEquals(self.ed.GetColumn(0), 0) + self.assertEquals(self.ed.GetColumn(1), 1) + # Next line caused infinite loop in 1.71 + self.assertEquals(self.ed.GetColumn(2), 1) + self.assertEquals(self.ed.GetColumn(3), 1) + + def testTabWidth(self): + self.assertEquals(self.ed.TabWidth, 8) + self.ed.AddText(3, b"x\tb") + self.assertEquals(self.ed.GetColumn(0), 0) + self.assertEquals(self.ed.GetColumn(1), 1) + self.assertEquals(self.ed.GetColumn(2), 8) + for col in range(10): + if col == 0: + self.assertEquals(self.ed.FindColumn(0, col), 0) + elif col == 1: + self.assertEquals(self.ed.FindColumn(0, col), 1) + elif col == 8: + self.assertEquals(self.ed.FindColumn(0, col), 2) + elif col == 9: + self.assertEquals(self.ed.FindColumn(0, col), 3) + # else: + # self.assertEquals(self.ed.FindColumn(0, col), 1) + self.ed.TabWidth = 4 + self.assertEquals(self.ed.TabWidth, 4) + self.assertEquals(self.ed.GetColumn(0), 0) + self.assertEquals(self.ed.GetColumn(1), 1) + self.assertEquals(self.ed.GetColumn(2), 4) + + def testIndent(self): + self.assertEquals(self.ed.Indent, 0) + self.assertEquals(self.ed.UseTabs, 1) + self.ed.Indent = 8 + self.ed.UseTabs = 0 + self.assertEquals(self.ed.Indent, 8) + self.assertEquals(self.ed.UseTabs, 0) + self.ed.AddText(3, b"x\tb") + self.assertEquals(self.ed.GetLineIndentation(0), 0) + self.ed.InsertText(0, b" ") + self.assertEquals(self.ed.GetLineIndentation(0), 1) + self.assertEquals(self.ed.GetLineIndentPosition(0), 1) + self.assertEquals(self.ed.Contents(), b" x\tb") + self.ed.SetLineIndentation(0,2) + self.assertEquals(self.ed.Contents(), b" x\tb") + self.assertEquals(self.ed.GetLineIndentPosition(0), 2) + self.ed.UseTabs = 1 + self.ed.SetLineIndentation(0,8) + self.assertEquals(self.ed.Contents(), b"\tx\tb") + self.assertEquals(self.ed.GetLineIndentPosition(0), 1) + + def testGetCurLine(self): + self.ed.AddText(1, b"x") + data = b"\0" * 100 + caret = self.ed.GetCurLine(len(data), data) + data = data.rstrip(b"\0") + self.assertEquals(caret, 1) + self.assertEquals(data, b"x") + + def testGetLine(self): + self.ed.AddText(1, b"x") + data = b"\0" * 100 + length = self.ed.GetLine(0, data) + self.assertEquals(length, 1) + data = data[:length] + self.assertEquals(data, b"x") + + def testLineEnds(self): + self.ed.AddText(3, b"x\ny") + self.assertEquals(self.ed.GetLineEndPosition(0), 1) + self.assertEquals(self.ed.GetLineEndPosition(1), 3) + self.assertEquals(self.ed.LineLength(0), 2) + self.assertEquals(self.ed.LineLength(1), 1) + self.assertEquals(self.ed.EOLMode, self.ed.SC_EOL_CRLF) + lineEnds = [b"\r\n", b"\r", b"\n"] + for lineEndType in [self.ed.SC_EOL_CR, self.ed.SC_EOL_LF, self.ed.SC_EOL_CRLF]: + self.ed.EOLMode = lineEndType + self.assertEquals(self.ed.EOLMode, lineEndType) + self.ed.ConvertEOLs(lineEndType) + self.assertEquals(self.ed.Contents(), b"x" + lineEnds[lineEndType] + b"y") + self.assertEquals(self.ed.LineLength(0), 1 + len(lineEnds[lineEndType])) + + def testGoto(self): + self.ed.AddText(5, b"a\nb\nc") + self.assertEquals(self.ed.CurrentPos, 5) + self.ed.GotoLine(1) + self.assertEquals(self.ed.CurrentPos, 2) + self.ed.GotoPos(4) + self.assertEquals(self.ed.CurrentPos, 4) + + def testCutCopyPaste(self): + self.ed.AddText(5, b"a1b2c") + self.ed.SetSel(1,3) + self.ed.Cut() + self.assertEquals(self.ed.CanPaste(), 1) + self.ed.SetSel(0, 0) + self.ed.Paste() + self.assertEquals(self.ed.Contents(), b"1ba2c") + self.ed.SetSel(4,5) + self.ed.Copy() + self.ed.SetSel(1,3) + self.ed.Paste() + self.assertEquals(self.ed.Contents(), b"1c2c") + self.ed.SetSel(2,4) + self.ed.Clear() + self.assertEquals(self.ed.Contents(), b"1c") + + def testGetSet(self): + self.ed.SetText(0, b"abc") + self.assertEquals(self.ed.TextLength, 3) + result = b"\0" * 5 + length = self.ed.GetText(4, result) + result = result[:length] + self.assertEquals(result, b"abc") + + def testAppend(self): + self.ed.SetText(0, b"abc") + self.assertEquals(self.ed.SelectionStart, 0) + self.assertEquals(self.ed.SelectionEnd, 0) + text = b"12" + self.ed.AppendText(len(text), text) + self.assertEquals(self.ed.SelectionStart, 0) + self.assertEquals(self.ed.SelectionEnd, 0) + self.assertEquals(self.ed.Contents(), b"abc12") + + def testTarget(self): + self.ed.SetText(0, b"abcd") + self.ed.TargetStart = 1 + self.ed.TargetEnd = 3 + self.assertEquals(self.ed.TargetStart, 1) + self.assertEquals(self.ed.TargetEnd, 3) + rep = b"321" + self.ed.ReplaceTarget(len(rep), rep) + self.assertEquals(self.ed.Contents(), b"a321d") + self.ed.SearchFlags = self.ed.SCFIND_REGEXP + self.assertEquals(self.ed.SearchFlags, self.ed.SCFIND_REGEXP) + searchString = b"([1-9]+)" # were \( \) is original regex algorithm. + pos = self.ed.SearchInTarget(len(searchString), searchString) + self.assertEquals(1, pos) + tagString = b"abcdefghijklmnop" + lenTag = self.ed.GetTag(1, tagString) + tagString = tagString[:lenTag] + self.assertEquals(tagString, b"321") + rep = b"\\1" + self.ed.TargetStart = 0 + self.ed.TargetEnd = 0 + self.ed.ReplaceTargetRE(len(rep), rep) + self.assertEquals(self.ed.Contents(), b"321a321d") + self.ed.SetSel(4,5) + self.ed.TargetFromSelection() + self.assertEquals(self.ed.TargetStart, 4) + self.assertEquals(self.ed.TargetEnd, 5) + + def testTargetEscape(self): + # Checks that a literal \ can be in the replacement. Bug #2959876 + self.ed.SetText(0, b"abcd") + self.ed.TargetStart = 1 + self.ed.TargetEnd = 3 + rep = b"\\\\n" + self.ed.ReplaceTargetRE(len(rep), rep) + self.assertEquals(self.ed.Contents(), b"a\\nd") + + def testPointsAndPositions(self): + self.ed.AddText(1, b"x") + # Start of text + self.assertEquals(self.ed.PositionFromPoint(0,0), 0) + # End of text + self.assertEquals(self.ed.PositionFromPoint(0,100), 1) + + def testLinePositions(self): + text = b"ab\ncd\nef" + nl = b"\n" + if sys.version_info[0] == 3: + nl = ord(b"\n") + self.ed.AddText(len(text), text) + self.assertEquals(self.ed.LineFromPosition(-1), 0) + line = 0 + for pos in range(len(text)+1): + self.assertEquals(self.ed.LineFromPosition(pos), line) + if pos < len(text) and text[pos] == nl: + line += 1 + + def testWordPositions(self): + text = b"ab cd\tef" + self.ed.AddText(len(text), text) + self.assertEquals(self.ed.WordStartPosition(3, 0), 2) + self.assertEquals(self.ed.WordStartPosition(4, 0), 3) + self.assertEquals(self.ed.WordStartPosition(5, 0), 3) + self.assertEquals(self.ed.WordStartPosition(6, 0), 5) + + self.assertEquals(self.ed.WordEndPosition(2, 0), 3) + self.assertEquals(self.ed.WordEndPosition(3, 0), 5) + self.assertEquals(self.ed.WordEndPosition(4, 0), 5) + self.assertEquals(self.ed.WordEndPosition(5, 0), 6) + self.assertEquals(self.ed.WordEndPosition(6, 0), 8) + +MODI = 1 +UNDO = 2 +REDO = 4 + +class TestContainerUndo(unittest.TestCase): + + def setUp(self): + self.xite = XiteWin.xiteFrame + self.ed = self.xite.ed + self.ed.ClearAll() + self.ed.EmptyUndoBuffer() + self.data = b"xy" + + def UndoState(self): + return (MODI if self.ed.Modify else 0) | \ + (UNDO if self.ed.CanUndo() else 0) | \ + (REDO if self.ed.CanRedo() else 0) + + def testContainerActNoCoalesce(self): + self.ed.InsertText(0, self.data) + self.assertEquals(self.ed.Length, 2) + self.assertEquals(self.UndoState(), MODI | UNDO) + self.ed.AddUndoAction(5, 0) + self.ed.Undo() + self.assertEquals(self.ed.Length, 2) + self.assertEquals(self.UndoState(), MODI | UNDO | REDO) + self.ed.Redo() + self.assertEquals(self.ed.Length, 2) + self.assertEquals(self.UndoState(), MODI | UNDO) + self.ed.Undo() + + def testContainerActCoalesce(self): + self.ed.InsertText(0, self.data) + self.ed.AddUndoAction(5, 1) + self.ed.Undo() + self.assertEquals(self.ed.Length, 0) + self.assertEquals(self.UndoState(), REDO) + self.ed.Redo() + self.assertEquals(self.ed.Length, 2) + self.assertEquals(self.UndoState(), MODI | UNDO) + + def testContainerMultiStage(self): + self.ed.InsertText(0, self.data) + self.ed.AddUndoAction(5, 1) + self.ed.AddUndoAction(5, 1) + self.assertEquals(self.ed.Length, 2) + self.assertEquals(self.UndoState(), MODI | UNDO) + self.ed.Undo() + self.assertEquals(self.ed.Length, 0) + self.assertEquals(self.UndoState(), REDO) + self.ed.Redo() + self.assertEquals(self.ed.Length, 2) + self.assertEquals(self.UndoState(), MODI | UNDO) + self.ed.AddUndoAction(5, 1) + self.assertEquals(self.ed.Length, 2) + self.assertEquals(self.UndoState(), MODI | UNDO) + self.ed.Undo() + self.assertEquals(self.ed.Length, 0) + self.assertEquals(self.UndoState(), REDO) + + def testContainerMultiStageNoText(self): + self.ed.AddUndoAction(5, 1) + self.ed.AddUndoAction(5, 1) + self.assertEquals(self.UndoState(), MODI | UNDO) + self.ed.Undo() + self.assertEquals(self.UndoState(), REDO) + self.ed.Redo() + self.assertEquals(self.UndoState(), MODI | UNDO) + self.ed.AddUndoAction(5, 1) + self.assertEquals(self.UndoState(), MODI | UNDO) + self.ed.Undo() + self.assertEquals(self.UndoState(), REDO) + + def testContainerActCoalesceEnd(self): + self.ed.AddUndoAction(5, 1) + self.assertEquals(self.ed.Length, 0) + self.assertEquals(self.UndoState(), MODI | UNDO) + self.ed.InsertText(0, self.data) + self.assertEquals(self.ed.Length, 2) + self.assertEquals(self.UndoState(), MODI | UNDO) + self.ed.Undo() + self.assertEquals(self.ed.Length, 0) + self.assertEquals(self.UndoState(), REDO) + self.ed.Redo() + self.assertEquals(self.ed.Length, 2) + self.assertEquals(self.UndoState(), MODI | UNDO) + + def testContainerBetweenInsertAndInsert(self): + self.assertEquals(self.ed.Length, 0) + self.ed.InsertText(0, self.data) + self.assertEquals(self.ed.Length, 2) + self.assertEquals(self.UndoState(), MODI | UNDO) + self.ed.AddUndoAction(5, 1) + self.assertEquals(self.ed.Length, 2) + self.assertEquals(self.UndoState(), MODI | UNDO) + self.ed.InsertText(2, self.data) + self.assertEquals(self.ed.Length, 4) + self.assertEquals(self.UndoState(), MODI | UNDO) + # Undoes both insertions and the containerAction in the middle + self.ed.Undo() + self.assertEquals(self.ed.Length, 0) + self.assertEquals(self.UndoState(), REDO) + + def testContainerNoCoalesceBetweenInsertAndInsert(self): + self.assertEquals(self.ed.Length, 0) + self.ed.InsertText(0, self.data) + self.assertEquals(self.ed.Length, 2) + self.assertEquals(self.UndoState(), MODI | UNDO) + self.ed.AddUndoAction(5, 0) + self.assertEquals(self.ed.Length, 2) + self.assertEquals(self.UndoState(), MODI | UNDO) + self.ed.InsertText(2, self.data) + self.assertEquals(self.ed.Length, 4) + self.assertEquals(self.UndoState(), MODI | UNDO) + # Undo last insertion + self.ed.Undo() + self.assertEquals(self.ed.Length, 2) + self.assertEquals(self.UndoState(), MODI | UNDO | REDO) + # Undo container + self.ed.Undo() + self.assertEquals(self.ed.Length, 2) + self.assertEquals(self.UndoState(), MODI | UNDO | REDO) + # Undo first insertion + self.ed.Undo() + self.assertEquals(self.ed.Length, 0) + self.assertEquals(self.UndoState(), REDO) + + def testContainerBetweenDeleteAndDelete(self): + self.ed.InsertText(0, self.data) + self.ed.EmptyUndoBuffer() + self.assertEquals(self.ed.Length, 2) + self.assertEquals(self.UndoState(), 0) + self.ed.SetSel(2,2) + self.ed.DeleteBack() + self.assertEquals(self.ed.Length, 1) + self.ed.AddUndoAction(5, 1) + self.ed.DeleteBack() + self.assertEquals(self.ed.Length, 0) + # Undoes both deletions and the containerAction in the middle + self.ed.Undo() + self.assertEquals(self.ed.Length, 2) + self.assertEquals(self.UndoState(), REDO) + + def testContainerBetweenInsertAndDelete(self): + self.assertEquals(self.ed.Length, 0) + self.ed.InsertText(0, self.data) + self.assertEquals(self.ed.Length, 2) + self.assertEquals(self.UndoState(), MODI | UNDO) + self.ed.AddUndoAction(5, 1) + self.assertEquals(self.UndoState(), MODI | UNDO) + self.ed.SetSel(0,1) + self.ed.Cut() + self.assertEquals(self.ed.Length, 1) + self.assertEquals(self.UndoState(), MODI | UNDO) + self.ed.Undo() # Only undoes the deletion + self.assertEquals(self.ed.Length, 2) + self.assertEquals(self.UndoState(), MODI | UNDO | REDO) + +class TestKeyCommands(unittest.TestCase): + """ These commands are normally assigned to keys and take no arguments """ + + def setUp(self): + self.xite = XiteWin.xiteFrame + self.ed = self.xite.ed + self.ed.ClearAll() + self.ed.EmptyUndoBuffer() + + def selRange(self): + return self.ed.CurrentPos, self.ed.Anchor + + def testLineMove(self): + self.ed.AddText(8, b"x1\ny2\nz3") + self.ed.SetSel(0,0) + self.ed.ChooseCaretX() + self.ed.LineDown() + self.ed.LineDown() + self.assertEquals(self.selRange(), (6, 6)) + self.ed.LineUp() + self.assertEquals(self.selRange(), (3, 3)) + self.ed.LineDownExtend() + self.assertEquals(self.selRange(), (6, 3)) + self.ed.LineUpExtend() + self.ed.LineUpExtend() + self.assertEquals(self.selRange(), (0, 3)) + + def testCharMove(self): + self.ed.AddText(8, b"x1\ny2\nz3") + self.ed.SetSel(0,0) + self.ed.CharRight() + self.ed.CharRight() + self.assertEquals(self.selRange(), (2, 2)) + self.ed.CharLeft() + self.assertEquals(self.selRange(), (1, 1)) + self.ed.CharRightExtend() + self.assertEquals(self.selRange(), (2, 1)) + self.ed.CharLeftExtend() + self.ed.CharLeftExtend() + self.assertEquals(self.selRange(), (0, 1)) + + def testWordMove(self): + self.ed.AddText(10, b"a big boat") + self.ed.SetSel(3,3) + self.ed.WordRight() + self.ed.WordRight() + self.assertEquals(self.selRange(), (10, 10)) + self.ed.WordLeft() + self.assertEquals(self.selRange(), (6, 6)) + self.ed.WordRightExtend() + self.assertEquals(self.selRange(), (10, 6)) + self.ed.WordLeftExtend() + self.ed.WordLeftExtend() + self.assertEquals(self.selRange(), (2, 6)) + + def testHomeEndMove(self): + self.ed.AddText(10, b"a big boat") + self.ed.SetSel(3,3) + self.ed.Home() + self.assertEquals(self.selRange(), (0, 0)) + self.ed.LineEnd() + self.assertEquals(self.selRange(), (10, 10)) + self.ed.SetSel(3,3) + self.ed.HomeExtend() + self.assertEquals(self.selRange(), (0, 3)) + self.ed.LineEndExtend() + self.assertEquals(self.selRange(), (10, 3)) + + def testStartEndMove(self): + self.ed.AddText(10, b"a\nbig\nboat") + self.ed.SetSel(3,3) + self.ed.DocumentStart() + self.assertEquals(self.selRange(), (0, 0)) + self.ed.DocumentEnd() + self.assertEquals(self.selRange(), (10, 10)) + self.ed.SetSel(3,3) + self.ed.DocumentStartExtend() + self.assertEquals(self.selRange(), (0, 3)) + self.ed.DocumentEndExtend() + self.assertEquals(self.selRange(), (10, 3)) + + +class TestMarkers(unittest.TestCase): + + def setUp(self): + self.xite = XiteWin.xiteFrame + self.ed = self.xite.ed + self.ed.ClearAll() + self.ed.EmptyUndoBuffer() + self.ed.AddText(5, b"x\ny\nz") + + def testMarker(self): + handle = self.ed.MarkerAdd(1,1) + self.assertEquals(self.ed.MarkerLineFromHandle(handle), 1) + self.ed.MarkerDelete(1,1) + self.assertEquals(self.ed.MarkerLineFromHandle(handle), -1) + + # def testTwiceAddedDelete(self): + # handle = self.ed.MarkerAdd(1,1) + # self.assertEquals(self.ed.MarkerGet(1), 2) + # handle2 = self.ed.MarkerAdd(1,1) + # self.assertEquals(self.ed.MarkerGet(1), 2) + # self.ed.MarkerDelete(1,1) + # self.assertEquals(self.ed.MarkerGet(1), 2) + # self.ed.MarkerDelete(1,1) + # self.assertEquals(self.ed.MarkerGet(1), 0) + + def testMarkerDeleteAll(self): + h1 = self.ed.MarkerAdd(0,1) + h2 = self.ed.MarkerAdd(1,2) + self.assertEquals(self.ed.MarkerLineFromHandle(h1), 0) + self.assertEquals(self.ed.MarkerLineFromHandle(h2), 1) + self.ed.MarkerDeleteAll(1) + self.assertEquals(self.ed.MarkerLineFromHandle(h1), -1) + self.assertEquals(self.ed.MarkerLineFromHandle(h2), 1) + self.ed.MarkerDeleteAll(-1) + self.assertEquals(self.ed.MarkerLineFromHandle(h1), -1) + self.assertEquals(self.ed.MarkerLineFromHandle(h2), -1) + + def testMarkerDeleteHandle(self): + handle = self.ed.MarkerAdd(0,1) + self.assertEquals(self.ed.MarkerLineFromHandle(handle), 0) + self.ed.MarkerDeleteHandle(handle) + self.assertEquals(self.ed.MarkerLineFromHandle(handle), -1) + + def testMarkerBits(self): + self.assertEquals(self.ed.MarkerGet(0), 0) + self.ed.MarkerAdd(0,1) + self.assertEquals(self.ed.MarkerGet(0), 2) + self.ed.MarkerAdd(0,2) + self.assertEquals(self.ed.MarkerGet(0), 6) + + def testMarkerAddSet(self): + self.assertEquals(self.ed.MarkerGet(0), 0) + self.ed.MarkerAddSet(0,5) + self.assertEquals(self.ed.MarkerGet(0), 5) + self.ed.MarkerDeleteAll(-1) + + def testMarkerNext(self): + self.assertEquals(self.ed.MarkerNext(0, 2), -1) + h1 = self.ed.MarkerAdd(0,1) + h2 = self.ed.MarkerAdd(2,1) + self.assertEquals(self.ed.MarkerNext(0, 2), 0) + self.assertEquals(self.ed.MarkerNext(1, 2), 2) + self.assertEquals(self.ed.MarkerNext(2, 2), 2) + self.assertEquals(self.ed.MarkerPrevious(0, 2), 0) + self.assertEquals(self.ed.MarkerPrevious(1, 2), 0) + self.assertEquals(self.ed.MarkerPrevious(2, 2), 2) + + def testMarkerNegative(self): + self.assertEquals(self.ed.MarkerNext(-1, 2), -1) + + def testLineState(self): + self.assertEquals(self.ed.MaxLineState, 0) + self.assertEquals(self.ed.GetLineState(0), 0) + self.assertEquals(self.ed.GetLineState(1), 0) + self.assertEquals(self.ed.GetLineState(2), 0) + self.ed.SetLineState(1, 100) + self.assertNotEquals(self.ed.MaxLineState, 0) + self.assertEquals(self.ed.GetLineState(0), 0) + self.assertEquals(self.ed.GetLineState(1), 100) + self.assertEquals(self.ed.GetLineState(2), 0) + + def testSymbolRetrieval(self): + self.ed.MarkerDefine(1,3) + self.assertEquals(self.ed.MarkerSymbolDefined(1), 3) + +class TestIndicators(unittest.TestCase): + + def setUp(self): + self.xite = XiteWin.xiteFrame + self.ed = self.xite.ed + self.ed.ClearAll() + self.ed.EmptyUndoBuffer() + + def testSetIndicator(self): + self.assertEquals(self.ed.IndicGetStyle(0), 1) + self.assertEquals(self.ed.IndicGetFore(0), 0x007f00) + self.ed.IndicSetStyle(0, 2) + self.ed.IndicSetFore(0, 0xff0080) + self.assertEquals(self.ed.IndicGetStyle(0), 2) + self.assertEquals(self.ed.IndicGetFore(0), 0xff0080) + + def testIndicatorFill(self): + self.ed.InsertText(0, b"abc") + self.ed.IndicatorCurrent = 3 + self.ed.IndicatorFillRange(1,1) + self.assertEquals(self.ed.IndicatorValueAt(3, 0), 0) + self.assertEquals(self.ed.IndicatorValueAt(3, 1), 1) + self.assertEquals(self.ed.IndicatorValueAt(3, 2), 0) + self.assertEquals(self.ed.IndicatorStart(3, 0), 0) + self.assertEquals(self.ed.IndicatorEnd(3, 0), 1) + self.assertEquals(self.ed.IndicatorStart(3, 1), 1) + self.assertEquals(self.ed.IndicatorEnd(3, 1), 2) + self.assertEquals(self.ed.IndicatorStart(3, 2), 2) + self.assertEquals(self.ed.IndicatorEnd(3, 2), 3) + + # def testIndicatorAtEnd(self): + # self.ed.InsertText(0, b"ab") + # self.ed.IndicatorCurrent = 3 + # self.ed.IndicatorFillRange(1,1) + # self.assertEquals(self.ed.IndicatorValueAt(3, 0), 0) + # self.assertEquals(self.ed.IndicatorValueAt(3, 1), 1) + # self.assertEquals(self.ed.IndicatorStart(3, 0), 0) + # self.assertEquals(self.ed.IndicatorEnd(3, 0), 1) + # self.assertEquals(self.ed.IndicatorStart(3, 1), 1) + # self.assertEquals(self.ed.IndicatorEnd(3, 1), 2) + # self.ed.DeleteRange(1, 1) + # # Now only one character left and does not have indicator so indicator 3 is null + # self.assertEquals(self.ed.IndicatorValueAt(3, 0), 0) + # # Since null, remaining calls return 0 + # self.assertEquals(self.ed.IndicatorStart(3, 0), 0) + # self.assertEquals(self.ed.IndicatorEnd(3, 0), 0) + # self.assertEquals(self.ed.IndicatorStart(3, 1), 0) + # self.assertEquals(self.ed.IndicatorEnd(3, 1), 0) + +class TestScrolling(unittest.TestCase): + + def setUp(self): + self.xite = XiteWin.xiteFrame + self.ed = self.xite.ed + self.ed.ClearAll() + self.ed.EmptyUndoBuffer() + # 150 should be enough lines + self.ed.InsertText(0, b"a\n" * 150) + + def testTop(self): + self.ed.GotoLine(0) + self.assertEquals(self.ed.FirstVisibleLine, 0) + + def testLineScroll(self): + self.ed.GotoLine(0) + self.ed.LineScroll(0, 3) + self.assertEquals(self.ed.FirstVisibleLine, 3) + + def testVisibleLine(self): + self.ed.FirstVisibleLine = 7 + self.assertEquals(self.ed.FirstVisibleLine, 7) + + +class TestBoostRegExSearch(unittest.TestCase): + def setUp(self): + self.xite = XiteWin.xiteFrame + self.ed = self.xite.ed + self.ed.ClearAll() + self.ed.EmptyUndoBuffer() + + def testDotStarRegEx(self): + toFind = b".*" + direction = self.SearchDirectionForward(self.ed) + + flags = self.ed.SCFIND_REGEXP | self.ed.SCFIND_REGEXP_SKIPCRLFASONE + self.doSearchTests(b"[aaAaaaBaCD]\r\n\r\n[aaa]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_NONE, direction) + self.doSearchTests(b"[aaAaaaBaCD]\r\n[]\r\n[aaa]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH, direction) + self.doSearchTests(b"[aaAaaaBaCD][]\r\n[]\r\n[aaa][]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_ALL, direction) + + flags |= self.ed.SCFIND_REGEXP_DOTMATCHESNL + self.doSearchTests(b"[aaAaaaBaCD\r\n\r\naaa]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_NONE, direction) + self.doSearchTests(b"[aaAaaaBaCD\r\n\r\naaa]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH, direction) + self.doSearchTests(b"[aaAaaaBaCD\r\n\r\naaa][]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_ALL, direction) + + flags = self.ed.SCFIND_REGEXP + self.doSearchTests(b"[aaAaaaBaCD]\r[]\n[]\r[]\n[aaa]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH, direction) + + def testDotStarRegExBackward(self): + toFind = b".*" + direction = self.SearchDirectionBackward(self.ed) + + flags = self.ed.SCFIND_REGEXP | self.ed.SCFIND_REGEXP_SKIPCRLFASONE + self.doSearchTests(b"[aaAaaaBaCD]\r\n\r\n[aaa]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_NONE, direction) + self.doSearchTests(b"[aaAaaaBaCD]\r\n[]\r\n[aaa]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH, direction) + self.doSearchTests(b"[][aaAaaaBaCD]\r\n[]\r\n[][aaa]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_ALL, direction) + + flags |= self.ed.SCFIND_REGEXP_DOTMATCHESNL + self.doSearchTests(b"[aaAaaaBaCD\r\n\r\naaa]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_NONE, direction) + self.doSearchTests(b"[aaAaaaBaCD\r\n\r\naaa]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH, direction) + self.doSearchTests(b"[][aaAaaaBaCD\r\n\r\naaa]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_ALL, direction) + + flags = self.ed.SCFIND_REGEXP + self.doSearchTests(b"[aaAaaaBaCD]\r[]\n[]\r[]\n[aaa]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH, direction) + + def testAStarRegEx(self): + toFind = b"a*" + direction = self.SearchDirectionForward(self.ed) + + flags = self.ed.SCFIND_REGEXP | self.ed.SCFIND_REGEXP_SKIPCRLFASONE | self.ed.SCFIND_MATCHCASE + self.doSearchTests(b"X[aa]A[aaa]B[a]CD\r\n\r\n[aaa]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_NONE, direction) + self.doSearchTests(b"[]X[aa]A[aaa]B[a]C[]D[]\r\n[]\r\n[aaa]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH, direction) + self.doSearchTests(b"[]X[aa][]A[aaa][]B[a][]C[]D[]\r\n[]\r\n[aaa][]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_ALL, direction) + + flags = self.ed.SCFIND_REGEXP | self.ed.SCFIND_MATCHCASE + self.doSearchTests(b"[]X[aa]A[aaa]B[a]C[]D[]\r[]\n[]\r[]\n[aaa]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH, direction) + + flags = self.ed.SCFIND_REGEXP | self.ed.SCFIND_REGEXP_SKIPCRLFASONE + self.doSearchTests(b"X[aaAaaa]B[a]CD\r\n\r\n[aaa]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_NONE, direction) + self.doSearchTests(b"[]X[aaAaaa]B[a]C[]D[]\r\n[]\r\n[aaa]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH, direction) + self.doSearchTests(b"[]X[aaAaaa][]B[a][]C[]D[]\r\n[]\r\n[aaa][]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_ALL, direction) + + def testAStarRegExBackward(self): + toFind = b"a*" + direction = self.SearchDirectionBackward(self.ed) + + flags = self.ed.SCFIND_REGEXP | self.ed.SCFIND_REGEXP_SKIPCRLFASONE | self.ed.SCFIND_MATCHCASE + self.doSearchTests(b"[aa]A[aaa]B[a]CD\r\n\r\n[aaa]X", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_NONE, direction) + self.doSearchTests(b"[aa]A[aaa]B[a]C[]D[]\r\n[]\r\n[aaa]X[]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH, direction) + self.doSearchTests(b"[][aa]A[][aaa]B[][a]C[]D[]\r\n[]\r\n[][aaa]X[]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_ALL, direction) + + flags = self.ed.SCFIND_REGEXP | self.ed.SCFIND_MATCHCASE + self.doSearchTests(b"[aa]A[aaa]B[a]C[]D[]\r[]\n[]\r[]\n[aaa]X[]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH, direction) + + flags = self.ed.SCFIND_REGEXP | self.ed.SCFIND_REGEXP_SKIPCRLFASONE + self.doSearchTests(b"[aaAaaa]B[a]CD\r\n\r\n[aaa]X", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_NONE, direction) + self.doSearchTests(b"[aaAaaa]B[a]C[]D[]\r\n[]\r\n[aaa]X[]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH, direction) + self.doSearchTests(b"[][aaAaaa]B[][a]C[]D[]\r\n[]\r\n[][aaa]X[]", toFind, flags | self.ed.SCFIND_REGEXP_EMPTYMATCH_ALL, direction) + + def testLineBeginEndRegEx(self): + flags = self.ed.SCFIND_REGEXP | self.ed.SCFIND_REGEXP_SKIPCRLFASONE | self.ed.SCFIND_REGEXP_EMPTYMATCH_ALL + direction = self.SearchDirectionForward(self.ed) + self.doSearchTests(b"[]ab\n[]ab\r[]ab\r\n[]ab\n[]\n[]ab\r[]\r[]ab\r\n[]\r\n[]ab", b"^", flags, direction) + self.doSearchTests(b"ab[]\nab[]\rab[]\r\nab[]\n[]\nab[]\r[]\rab[]\r\n[]\r\nab[]", b"$", flags, direction) + self.doSearchTests(b"[]", b"^", flags, direction) + self.doSearchTests(b"[]", b"$", flags, direction) + + direction = self.SearchDirectionBackwardExceptFirstAndLastCharacter(self.ed) + self.doSearchTests(b"ab\n[]ab\r[]ab\r\n[]ab\n[]\n[]ab\r[]\r[]ab\r\n[]\r\n[]ab", b"^", flags, direction) + self.doSearchTests(b"ab[]\nab[]\rab[]\r\nab[]\n[]\nab[]\r[]\rab[]\r\n[]\r\nab", b"$", flags, direction) + + def testLineBeginEndRegExBackward(self): + flags = self.ed.SCFIND_REGEXP | self.ed.SCFIND_REGEXP_SKIPCRLFASONE | self.ed.SCFIND_REGEXP_EMPTYMATCH_ALL + direction = self.SearchDirectionBackward(self.ed) + self.doSearchTests(b"[]ab\n[]ab\r[]ab\r\n[]ab\n[]\n[]ab\r[]\r[]ab\r\n[]\r\n[]ab", b"^", flags, direction) + self.doSearchTests(b"ab[]\nab[]\rab[]\r\nab[]\n[]\nab[]\r[]\rab[]\r\n[]\r\nab[]", b"$", flags, direction) + self.doSearchTests(b"[]", b"^", flags, direction) + self.doSearchTests(b"[]", b"$", flags, direction) + + direction = self.SearchDirectionBackwardExceptFirstAndLastCharacter(self.ed) + self.doSearchTests(b"ab\n[]ab\r[]ab\r\n[]ab\n[]\n[]ab\r[]\r[]ab\r\n[]\r\n[]ab", b"^", flags, direction) + self.doSearchTests(b"ab[]\nab[]\rab[]\r\nab[]\n[]\nab[]\r[]\rab[]\r\n[]\r\nab", b"$", flags, direction) + + def testUtf8RegEx(self): + self.ed.SetCodePage(self.ed.SC_CP_UTF8) + self.doSearchTests(b"[aa]b[éé]b[aé]b[éÉ]ááá[Éaéé]b", b"[aéÉ]+", self.ed.SCFIND_REGEXP, self.SearchDirectionForward(self.ed)) + self.doSearchTests(b"[áéíóú]\r\n[]\r\n[áé]", b".*", self.ed.SCFIND_REGEXP | self.ed.SCFIND_REGEXP_SKIPCRLFASONE | self.ed.SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH, self.SearchDirectionForward(self.ed)) + + def testUtf8RegExBackward(self): + self.ed.SetCodePage(self.ed.SC_CP_UTF8) + self.doSearchTests(b"[aa]b[éé]b[aé]b[éÉ]ááá[Éaéé]b", b"[aéÉ]+", self.ed.SCFIND_REGEXP, self.SearchDirectionBackward(self.ed)) + self.doSearchTests(b"[áéíóú]\r\n[]\r\n[áé]", b".*", self.ed.SCFIND_REGEXP | self.ed.SCFIND_REGEXP_SKIPCRLFASONE | self.ed.SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH, self.SearchDirectionBackward(self.ed)) + + def testOverlapRegEx(self): + flags = self.ed.SCFIND_REGEXP | self.ed.SCFIND_MATCHCASE + toFind = b"abc|bcde" + self.doSearchTests(b"[abc]def[abc]ab[bcde]f", toFind, flags, self.SearchDirectionForward(self.ed)) + self.doSearchTests(b"a[bcde]f[abc]ab[bcde]f", toFind, flags, self.SearchDirectionBackward(self.ed)) + toFind = b"abcde|bc" + self.doSearchTests(b"[abcde]fa[bc]ab[bc]def", toFind, flags, self.SearchDirectionForward(self.ed)) + self.doSearchTests(b"[abcde]fa[bc]ab[bc]def", toFind, flags, self.SearchDirectionBackward(self.ed)) + + def testDocumentModificationBetweenSearch(self): + flags = self.ed.SCFIND_REGEXP | self.ed.SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH + toFind = b"a*" + self.setText(b"BaaC") ; firstMatch = (0,0,0) ; secondMatch = (1,1,3) + def searchFromStart(): return self.ed.FindBytes2(0, self.ed.Length, toFind, flags) + self.assertEquals(firstMatch, searchFromStart()) + self.assertEquals(secondMatch, searchFromStart()) # This search should find second match, even though it is from same start position, as it should be considered a continuation of previous search. + self.assertEquals(firstMatch, searchFromStart()) # A third search from same start should find first match, as it is not a continuation of previous search. + self.setText(b"Baa") + self.assertEquals(firstMatch, searchFromStart()) # This search should find first match, as it should not be considered a continuation since text has been modified. + + + def testAStarAllowEmptyAtStartRegEx(self): + flags = self.ed.SCFIND_REGEXP | self.ed.SCFIND_REGEXP_EMPTYMATCH_ALL | self.ed.SCFIND_REGEXP_EMPTYMATCH_ALLOWATSTART + toFind = b"a*" + self.setText(b"BaaC") + firstMatch = (0,0,0) + secondMatch = (1,1,3) + def searchFromStart(): return self.ed.FindBytes2(0, self.ed.Length, toFind, flags) + self.assertEquals(firstMatch, searchFromStart()) + self.assertEquals(firstMatch, searchFromStart()) # This search would normally find second match, even though it is from same start position, + # as it should be considered a continuation of previous search, but we have set SCFIND_REGEXP_EMPTYMATCH_ALLOWATSTART + # which will force it to allow the first match to be found again. + + + # 'textWithBrackets' should contain the text with [] brackets around all strings that should be found by successive search of 'whatToFind', each search starting at end of previous match. + def doSearchTests(self, textWithBrackets, whatToFind, flags, direction): + expectedResults = self.setupTest(textWithBrackets, direction) + currentPosition = direction.startPosition(); i = 1 + for expected in direction.orderResults(expectedResults): + result = self.ed.FindBytes2(currentPosition, direction.endPosition(), whatToFind, flags) + self.assertEquals(expected, result, self.failureString(expected, result, i)) + currentPosition = direction.nextPositionFromResult(expected); i += 1 + + def failureString(self, expected, result, i): + class MapCrLf: + def __getitem__(self, c): + if c == ord('\r'): return u'«' + if c == ord('\n'): return u'¬' + return c + return ("%s != %s (at search #%d)\n"%(str(expected), str(result), i) \ + + unicode(self.text, 'utf-8').translate(MapCrLf()) + "\n" \ + + self.markCharRange(expected) + "\n" \ + + self.markCharRange(result) \ + ).encode('utf-8') + + def markCharRange(self, searchResult): + if searchResult[0] < 0: return "(No match)" + left = searchResult[1]; right = searchResult[2] + self.assertTrue(left <= right, "Invalid range (%d,%d)"%(left,right)) + leftChar = self.bytePositionToCharacterPosition(left) + returnValue = ' '*leftChar + if left == right: + if not self.positionError: returnValue += '^' + else: returnValue += '~' + else: + if not self.positionError: returnValue += '[' + else: returnValue += '~' + rightChar = self.bytePositionToCharacterPosition(right) + returnValue += ' '*(rightChar-leftChar-1) + if not self.positionError: returnValue += ']' + else: returnValue += '~' + return returnValue + + def bytePositionToCharacterPosition(self, bytePosition): + self.positionError = False + while True: + try: + return len(unicode(self.text[:bytePosition], 'utf-8')) + except UnicodeDecodeError: + self.positionError = True + bytePosition -= 1 + + class SearchDirectionForward: + def __init__(self, editorObject): self.ed = editorObject + def startPosition(self): return 0 + def endPosition(self): return self.ed.Length + def nextPositionFromResult(self, searchResult): return searchResult[2] + def orderResults(self, expectedResults): return expectedResults + def atStart(self): return [] + def atEnd(self): return [(-1, 0, 0)] # Last search should fail. + + class SearchDirectionBackward: + def __init__(self, editorObject): self.ed = editorObject + def startPosition(self): return self.ed.Length + def endPosition(self): return 0 + def nextPositionFromResult(self, searchResult): return searchResult[1] + def orderResults(self, expectedResults): return reversed(expectedResults) + def atStart(self): return [(-1, 0, 0)] # Last search should fail. + def atEnd(self): return [] + + class SearchDirectionForwardExceptFirstAndLastCharacter(SearchDirectionForward): + def startPosition(self): return 1 + def endPosition(self): return self.ed.Length-1 + + class SearchDirectionBackwardExceptFirstAndLastCharacter(SearchDirectionBackward): + def startPosition(self): return self.ed.Length-1 + def endPosition(self): return 1 + + def setupTest(self, textWithBrackets, direction): + expectedResults = self.getExpectedResults(textWithBrackets, direction) + text = self.removeBrackets(textWithBrackets) + self.setText(text) + return expectedResults + + def getExpectedResults(self, textWithBrackets, direction): + textSplit = textWithBrackets.split(b'[') + currentPosition = 0 + expectedResults = [] + expectedResults += direction.atStart() + for i in range(0, len(textSplit)-1): + currentPosition += len(self.removeBrackets(textSplit[i])) + expectedResults += [(currentPosition, currentPosition, currentPosition+textSplit[i+1].find(b']'))] + expectedResults += direction.atEnd() + return expectedResults + + def removeBrackets(self, textWithBrackets): + return textWithBrackets.translate(None, b"[]") + + def setText(self, text): + self.text = text + self.ed.SetText(len(text), text) + +class TestBoostRegExReplace(unittest.TestCase): + def setUp(self): + self.xite = XiteWin.xiteFrame + self.ed = self.xite.ed + self.ed.ClearAll() + self.ed.EmptyUndoBuffer() + + def testReplace(self): + self.ed.SetCodePage(0) + self.setText(b"abcd1234XYZef5678Wgh") + self.doReplace(b"([0-9]+)([A-Z]+)", b"[\\2;\\1]", self.ed.SCFIND_REGEXP | self.ed.SCFIND_MATCHCASE) + self.assertEquals(self.ed.Contents(), b"abcd[XYZ;1234]ef5678Wgh") + + def testReplaceAStar(self): + self.setText(b"aaXYaZaaa") + flags = self.ed.SCFIND_REGEXP | self.ed.SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH + position = 0 + position = self.doReplace(b"a*", b"b", flags, position) + self.assertEquals(self.ed.Contents(), b"bXYaZaaa") + self.assertEquals(position, 1) + position = self.doReplace(b"a*", b"c", flags, position) + self.assertEquals(self.ed.Contents(), b"bXcYaZaaa") + self.assertEquals(position, 3) + position = self.doReplace(b"a*", b"d", flags, position) + self.assertEquals(self.ed.Contents(), b"bXcYdZaaa") + self.assertEquals(position, 5) + position = self.doReplace(b"a*", b"e", flags, position) + self.assertEquals(self.ed.Contents(), b"bXcYdZe") + self.assertEquals(position, 7) + position = self.doReplace(b"a*", b"f", flags, position) + self.assertEquals(self.ed.Contents(), b"bXcYdZe") + self.assertEquals(position, -1) + + self.setText(b"aaXYa") + flags = self.ed.SCFIND_REGEXP | self.ed.SCFIND_REGEXP_EMPTYMATCH_ALL + position = self.doReplace(b"a*", b"b", flags, position) + self.assertEquals(self.ed.Contents(), b"bXYa") + self.assertEquals(position, 1) + position = self.doReplace(b"a*", b"c", flags, position) + self.assertEquals(self.ed.Contents(), b"bcXYa") + self.assertEquals(position, 2) + position = self.doReplace(b"a*", b"d", flags, position) + self.assertEquals(self.ed.Contents(), b"bcXdYa") + self.assertEquals(position, 4) + position = self.doReplace(b"a*", b"e", flags, position) + self.assertEquals(self.ed.Contents(), b"bcXdYe") + self.assertEquals(position, 6) + position = self.doReplace(b"a*", b"f", flags, position) + self.assertEquals(self.ed.Contents(), b"bcXdYef") + self.assertEquals(position, 7) + position = self.doReplace(b"a*", b"g", flags, position) + self.assertEquals(self.ed.Contents(), b"bcXdYef") + self.assertEquals(position, -1) + + def testReplaceEOL(self): + self.doTestReplaceEOL(self.ed.SCFIND_REGEXP | self.ed.SCFIND_REGEXP_EMPTYMATCH_NOTAFTERMATCH) + self.doTestReplaceEOL(self.ed.SCFIND_REGEXP | self.ed.SCFIND_REGEXP_EMPTYMATCH_ALL) + + def doTestReplaceEOL(self, flags): + self.setText(b"ab\ncde\n\nfg") + position = 0 + position = self.doReplace(b"$", b"A", flags, position) + self.assertEquals(self.ed.Contents(), b"abA\ncde\n\nfg") + self.assertEquals(position, 3) + position = self.doReplace(b"$", b"B", flags, position) + self.assertEquals(self.ed.Contents(), b"abA\ncdeB\n\nfg") + self.assertEquals(position, 8) + position = self.doReplace(b"$", b"C", flags, position) + self.assertEquals(self.ed.Contents(), b"abA\ncdeB\nC\nfg") + self.assertEquals(position, 10) + position = self.doReplace(b"$", b"D", flags, position) + self.assertEquals(self.ed.Contents(), b"abA\ncdeB\nC\nfgD") + self.assertEquals(position, 14) + position = self.doReplace(b"$", b"E", flags, position) + self.assertEquals(self.ed.Contents(), b"abA\ncdeB\nC\nfgD") + self.assertEquals(position, -1) + + def testReplaceUtf8(self): + self.ed.SetCodePage(self.ed.SC_CP_UTF8) + self.setText(b"áéíó1234ÁÉÍáé5678Áae") + self.doReplace(b"([0-9]+)([ÁÉÍ]+)", b"[\\2;\\1]", self.ed.SCFIND_REGEXP | self.ed.SCFIND_MATCHCASE) + self.assertEquals(self.ed.Contents(), b"áéíó[ÁÉÍ;1234]áé5678Áae") + + def doReplace(self, whatToFind, replaceBy, flags, startSearchPosition = 0): + searchResult = self.ed.FindBytes2(startSearchPosition, self.ed.Length, whatToFind, flags) + if searchResult[0] < 0: return -1 + self.ed.TargetStart = searchResult[1] + self.ed.TargetEnd = searchResult[2] + replacementLength = self.ed.ReplaceTargetRE(len(replaceBy), replaceBy) + return searchResult[1] + replacementLength + + def setText(self, text): + self.ed.SetText(len(text), text) + +class TestSearch(unittest.TestCase): + + def setUp(self): + self.xite = XiteWin.xiteFrame + self.ed = self.xite.ed + self.ed.ClearAll() + self.ed.EmptyUndoBuffer() + self.ed.InsertText(0, b"a\tbig boat\t") + + def testFind(self): + pos = self.ed.FindBytes(0, self.ed.Length, b"zzz", 0) + self.assertEquals(pos, -1) + pos = self.ed.FindBytes(0, self.ed.Length, b"big", 0) + self.assertEquals(pos, 2) + + def testFindEmpty(self): + pos = self.ed.FindBytes(0, self.ed.Length, b"", 0) + self.assertEquals(pos, 0) + + def testCaseFind(self): + self.assertEquals(self.ed.FindBytes(0, self.ed.Length, b"big", 0), 2) + self.assertEquals(self.ed.FindBytes(0, self.ed.Length, b"bIg", 0), 2) + self.assertEquals(self.ed.FindBytes(0, self.ed.Length, b"bIg", + self.ed.SCFIND_MATCHCASE), -1) + + def testWordFind(self): + self.assertEquals(self.ed.FindBytes(0, self.ed.Length, b"bi", 0), 2) + self.assertEquals(self.ed.FindBytes(0, self.ed.Length, b"bi", + self.ed.SCFIND_WHOLEWORD), -1) + + def testWordStartFind(self): + self.assertEquals(self.ed.FindBytes(0, self.ed.Length, b"bi", 0), 2) + self.assertEquals(self.ed.FindBytes(0, self.ed.Length, b"bi", + self.ed.SCFIND_WORDSTART), 2) + self.assertEquals(self.ed.FindBytes(0, self.ed.Length, b"ig", 0), 3) + self.assertEquals(self.ed.FindBytes(0, self.ed.Length, b"ig", + self.ed.SCFIND_WORDSTART), -1) + + def testREFind(self): + flags = self.ed.SCFIND_REGEXP + self.assertEquals(-1, self.ed.FindBytes(0, self.ed.Length, b"b.g", 0)) + self.assertEquals(2, self.ed.FindBytes(0, self.ed.Length, b"b.g", flags)) + self.assertEquals(2, self.ed.FindBytes(0, self.ed.Length, b"\", flags)) + self.assertEquals(-1, self.ed.FindBytes(0, self.ed.Length, b"b[A-Z]g", + flags | self.ed.SCFIND_MATCHCASE)) + self.assertEquals(2, self.ed.FindBytes(0, self.ed.Length, b"b[a-z]g", flags)) + self.assertEquals(6, self.ed.FindBytes(0, self.ed.Length, b"b[a-z]*t", flags)) + self.assertEquals(0, self.ed.FindBytes(0, self.ed.Length, b"^a", flags)) + self.assertEquals(10, self.ed.FindBytes(0, self.ed.Length, b"\t$", flags)) + self.assertEquals(0, self.ed.FindBytes(0, self.ed.Length, b"([a]).*\0", flags)) # were \( \) in original regex algorithm. + + def testPosixREFind(self): + flags = self.ed.SCFIND_REGEXP | self.ed.SCFIND_POSIX + self.assertEquals(-1, self.ed.FindBytes(0, self.ed.Length, b"b.g", 0)) + self.assertEquals(2, self.ed.FindBytes(0, self.ed.Length, b"b.g", flags)) + self.assertEquals(2, self.ed.FindBytes(0, self.ed.Length, b"\", flags)) + self.assertEquals(-1, self.ed.FindBytes(0, self.ed.Length, b"b[A-Z]g", + flags | self.ed.SCFIND_MATCHCASE)) + self.assertEquals(2, self.ed.FindBytes(0, self.ed.Length, b"b[a-z]g", flags)) + self.assertEquals(6, self.ed.FindBytes(0, self.ed.Length, b"b[a-z]*t", flags)) + self.assertEquals(0, self.ed.FindBytes(0, self.ed.Length, b"^a", flags)) + self.assertEquals(10, self.ed.FindBytes(0, self.ed.Length, b"\t$", flags)) + self.assertEquals(0, self.ed.FindBytes(0, self.ed.Length, b"([a]).*\0", flags)) + + def testPhilippeREFind(self): + # Requires 1.,72 + flags = self.ed.SCFIND_REGEXP + self.assertEquals(0, self.ed.FindBytes(0, self.ed.Length, b"\w", flags)) + self.assertEquals(1, self.ed.FindBytes(0, self.ed.Length, b"\W", flags)) + self.assertEquals(-1, self.ed.FindBytes(0, self.ed.Length, b"\d", flags)) + self.assertEquals(0, self.ed.FindBytes(0, self.ed.Length, b"\D", flags)) + self.assertEquals(1, self.ed.FindBytes(0, self.ed.Length, b"\s", flags)) + self.assertEquals(0, self.ed.FindBytes(0, self.ed.Length, b"\S", flags)) + self.assertEquals(2, self.ed.FindBytes(0, self.ed.Length, b"\x62", flags)) + + def testRENonASCII(self): + self.ed.InsertText(0, b"\xAD") + flags = self.ed.SCFIND_REGEXP + self.assertEquals(-1, self.ed.FindBytes(0, self.ed.Length, b"\\x10", flags)) + self.assertEquals(2, self.ed.FindBytes(0, self.ed.Length, b"\\x09", flags)) + self.assertEquals(-1, self.ed.FindBytes(0, self.ed.Length, b"\\xAB", flags)) + self.assertEquals(0, self.ed.FindBytes(0, self.ed.Length, b"\\xAD", flags)) + +class TestProperties(unittest.TestCase): + + def setUp(self): + self.xite = XiteWin.xiteFrame + self.ed = self.xite.ed + self.ed.ClearAll() + self.ed.EmptyUndoBuffer() + + def testSet(self): + self.ed.SetProperty(b"test", b"12") + self.assertEquals(self.ed.GetPropertyInt(b"test"), 12) + result = b"\0" * 10 + length = self.ed.GetProperty(b"test", result) + result = result[:length] + self.assertEquals(result, b"12") + self.ed.SetProperty(b"test.plus", b"[$(test)]") + result = b"\0" * 10 + length = self.ed.GetPropertyExpanded(b"test.plus", result) + result = result[:length] + self.assertEquals(result, b"[12]") + +class TestTextMargin(unittest.TestCase): + + def setUp(self): + self.xite = XiteWin.xiteFrame + self.ed = self.xite.ed + self.ed.ClearAll() + self.ed.EmptyUndoBuffer() + self.txt = b"abcd" + self.ed.AddText(1, b"x") + + def testAscent(self): + lineHeight = self.ed.TextHeight(0) + self.ed.ExtraAscent + self.assertEquals(self.ed.ExtraAscent, 0) + self.assertEquals(self.ed.ExtraDescent, 0) + self.ed.ExtraAscent = 1 + self.assertEquals(self.ed.ExtraAscent, 1) + self.ed.ExtraDescent = 2 + self.assertEquals(self.ed.ExtraDescent, 2) + # Allow line height to recalculate + self.xite.DoEvents() + lineHeightIncreased = self.ed.TextHeight(0) + self.assertEquals(lineHeightIncreased, lineHeight + 2 + 1) + + def testTextMargin(self): + self.ed.MarginSetText(0, self.txt) + result = b"\0" * 10 + length = self.ed.MarginGetText(0, result) + result = result[:length] + self.assertEquals(result, self.txt) + self.ed.MarginTextClearAll() + + def testTextMarginStyle(self): + self.ed.MarginSetText(0, self.txt) + self.ed.MarginSetStyle(0, 33) + self.assertEquals(self.ed.MarginGetStyle(0), 33) + self.ed.MarginTextClearAll() + + def testTextMarginStyles(self): + styles = b"\001\002\003\004" + self.ed.MarginSetText(0, self.txt) + self.ed.MarginSetStyles(0, styles) + result = b"\0" * 10 + length = self.ed.MarginGetStyles(0, result) + result = result[:length] + self.assertEquals(result, styles) + self.ed.MarginTextClearAll() + + def testTextMarginStyleOffset(self): + self.ed.MarginSetStyleOffset(300) + self.assertEquals(self.ed.MarginGetStyleOffset(), 300) + +class TestAnnotation(unittest.TestCase): + + def setUp(self): + self.xite = XiteWin.xiteFrame + self.ed = self.xite.ed + self.ed.ClearAll() + self.ed.EmptyUndoBuffer() + self.txt = b"abcd" + self.ed.AddText(1, b"x") + + def testTextAnnotation(self): + self.assertEquals(self.ed.AnnotationGetLines(), 0) + self.ed.AnnotationSetText(0, self.txt) + self.assertEquals(self.ed.AnnotationGetLines(), 1) + result = b"\0" * 10 + length = self.ed.AnnotationGetText(0, result) + self.assertEquals(length, 4) + result = result[:length] + self.assertEquals(result, self.txt) + self.ed.AnnotationClearAll() + + def testTextAnnotationStyle(self): + self.ed.AnnotationSetText(0, self.txt) + self.ed.AnnotationSetStyle(0, 33) + self.assertEquals(self.ed.AnnotationGetStyle(0), 33) + self.ed.AnnotationClearAll() + + def testTextAnnotationStyles(self): + styles = b"\001\002\003\004" + self.ed.AnnotationSetText(0, self.txt) + self.ed.AnnotationSetStyles(0, styles) + result = b"\0" * 10 + length = self.ed.AnnotationGetStyles(0, result) + result = result[:length] + self.assertEquals(result, styles) + self.ed.AnnotationClearAll() + + def testTextAnnotationStyleOffset(self): + self.ed.AnnotationSetStyleOffset(300) + self.assertEquals(self.ed.AnnotationGetStyleOffset(), 300) + + def testTextAnnotationVisible(self): + self.assertEquals(self.ed.AnnotationGetVisible(), 0) + self.ed.AnnotationSetVisible(2) + self.assertEquals(self.ed.AnnotationGetVisible(), 2) + self.ed.AnnotationSetVisible(0) + +class TestMultiSelection(unittest.TestCase): + + def setUp(self): + self.xite = XiteWin.xiteFrame + self.ed = self.xite.ed + self.ed.ClearAll() + self.ed.EmptyUndoBuffer() + # 3 lines of 3 characters + t = b"xxx\nxxx\nxxx" + self.ed.AddText(len(t), t) + + def testSelectionCleared(self): + self.ed.ClearSelections() + self.assertEquals(self.ed.Selections, 1) + self.assertEquals(self.ed.MainSelection, 0) + self.assertEquals(self.ed.GetSelectionNCaret(0), 0) + self.assertEquals(self.ed.GetSelectionNAnchor(0), 0) + + def test1Selection(self): + self.ed.SetSelection(1, 2) + self.assertEquals(self.ed.Selections, 1) + self.assertEquals(self.ed.MainSelection, 0) + self.assertEquals(self.ed.GetSelectionNCaret(0), 1) + self.assertEquals(self.ed.GetSelectionNAnchor(0), 2) + self.assertEquals(self.ed.GetSelectionNStart(0), 1) + self.assertEquals(self.ed.GetSelectionNEnd(0), 2) + self.ed.SwapMainAnchorCaret() + self.assertEquals(self.ed.Selections, 1) + self.assertEquals(self.ed.MainSelection, 0) + self.assertEquals(self.ed.GetSelectionNCaret(0), 2) + self.assertEquals(self.ed.GetSelectionNAnchor(0), 1) + + def test1SelectionReversed(self): + self.ed.SetSelection(2, 1) + self.assertEquals(self.ed.Selections, 1) + self.assertEquals(self.ed.MainSelection, 0) + self.assertEquals(self.ed.GetSelectionNCaret(0), 2) + self.assertEquals(self.ed.GetSelectionNAnchor(0), 1) + self.assertEquals(self.ed.GetSelectionNStart(0), 1) + self.assertEquals(self.ed.GetSelectionNEnd(0), 2) + + def test1SelectionByStartEnd(self): + self.ed.SetSelectionNStart(0, 2) + self.ed.SetSelectionNEnd(0, 3) + self.assertEquals(self.ed.Selections, 1) + self.assertEquals(self.ed.MainSelection, 0) + self.assertEquals(self.ed.GetSelectionNAnchor(0), 2) + self.assertEquals(self.ed.GetSelectionNCaret(0), 3) + self.assertEquals(self.ed.GetSelectionNStart(0), 2) + self.assertEquals(self.ed.GetSelectionNEnd(0), 3) + + def test2Selections(self): + self.ed.SetSelection(1, 2) + self.ed.AddSelection(4, 5) + self.assertEquals(self.ed.Selections, 2) + self.assertEquals(self.ed.MainSelection, 1) + self.assertEquals(self.ed.GetSelectionNCaret(0), 1) + self.assertEquals(self.ed.GetSelectionNAnchor(0), 2) + self.assertEquals(self.ed.GetSelectionNCaret(1), 4) + self.assertEquals(self.ed.GetSelectionNAnchor(1), 5) + self.assertEquals(self.ed.GetSelectionNStart(0), 1) + self.assertEquals(self.ed.GetSelectionNEnd(0), 2) + self.ed.MainSelection = 0 + self.assertEquals(self.ed.MainSelection, 0) + self.ed.RotateSelection() + self.assertEquals(self.ed.MainSelection, 1) + + def testRectangularSelection(self): + self.ed.RectangularSelectionAnchor = 1 + self.assertEquals(self.ed.RectangularSelectionAnchor, 1) + self.ed.RectangularSelectionCaret = 10 + self.assertEquals(self.ed.RectangularSelectionCaret, 10) + self.assertEquals(self.ed.Selections, 3) + self.assertEquals(self.ed.MainSelection, 2) + self.assertEquals(self.ed.GetSelectionNAnchor(0), 1) + self.assertEquals(self.ed.GetSelectionNCaret(0), 2) + self.assertEquals(self.ed.GetSelectionNAnchor(1), 5) + self.assertEquals(self.ed.GetSelectionNCaret(1), 6) + self.assertEquals(self.ed.GetSelectionNAnchor(2), 9) + self.assertEquals(self.ed.GetSelectionNCaret(2), 10) + + def testVirtualSpace(self): + self.ed.SetSelection(3, 7) + self.ed.SetSelectionNCaretVirtualSpace(0, 3) + self.assertEquals(self.ed.GetSelectionNCaretVirtualSpace(0), 3) + self.ed.SetSelectionNAnchorVirtualSpace(0, 2) + self.assertEquals(self.ed.GetSelectionNAnchorVirtualSpace(0), 2) + # Does not check that virtual space is valid by being at end of line + self.ed.SetSelection(1, 1) + self.ed.SetSelectionNCaretVirtualSpace(0, 3) + self.assertEquals(self.ed.GetSelectionNCaretVirtualSpace(0), 3) + + def testRectangularVirtualSpace(self): + self.ed.VirtualSpaceOptions=1 + self.ed.RectangularSelectionAnchor = 3 + self.assertEquals(self.ed.RectangularSelectionAnchor, 3) + self.ed.RectangularSelectionCaret = 7 + self.assertEquals(self.ed.RectangularSelectionCaret, 7) + self.ed.RectangularSelectionAnchorVirtualSpace = 1 + self.assertEquals(self.ed.RectangularSelectionAnchorVirtualSpace, 1) + self.ed.RectangularSelectionCaretVirtualSpace = 10 + self.assertEquals(self.ed.RectangularSelectionCaretVirtualSpace, 10) + self.assertEquals(self.ed.Selections, 2) + self.assertEquals(self.ed.MainSelection, 1) + self.assertEquals(self.ed.GetSelectionNAnchor(0), 3) + self.assertEquals(self.ed.GetSelectionNAnchorVirtualSpace(0), 1) + self.assertEquals(self.ed.GetSelectionNCaret(0), 3) + self.assertEquals(self.ed.GetSelectionNCaretVirtualSpace(0), 10) + + def testRectangularVirtualSpaceOptionOff(self): + # Same as previous test but virtual space option off so no virtual space in result + self.ed.VirtualSpaceOptions=0 + self.ed.RectangularSelectionAnchor = 3 + self.assertEquals(self.ed.RectangularSelectionAnchor, 3) + self.ed.RectangularSelectionCaret = 7 + self.assertEquals(self.ed.RectangularSelectionCaret, 7) + self.ed.RectangularSelectionAnchorVirtualSpace = 1 + self.assertEquals(self.ed.RectangularSelectionAnchorVirtualSpace, 1) + self.ed.RectangularSelectionCaretVirtualSpace = 10 + self.assertEquals(self.ed.RectangularSelectionCaretVirtualSpace, 10) + self.assertEquals(self.ed.Selections, 2) + self.assertEquals(self.ed.MainSelection, 1) + self.assertEquals(self.ed.GetSelectionNAnchor(0), 3) + self.assertEquals(self.ed.GetSelectionNAnchorVirtualSpace(0), 0) + self.assertEquals(self.ed.GetSelectionNCaret(0), 3) + self.assertEquals(self.ed.GetSelectionNCaretVirtualSpace(0), 0) + +class TestCaseMapping(unittest.TestCase): + def setUp(self): + self.xite = XiteWin.xiteFrame + self.ed = self.xite.ed + self.ed.ClearAll() + self.ed.EmptyUndoBuffer() + + def tearDown(self): + self.ed.SetCodePage(0) + self.ed.StyleSetCharacterSet(self.ed.STYLE_DEFAULT, self.ed.SC_CHARSET_DEFAULT) + + def testEmpty(self): + # Trying to upper case an empty string caused a crash at one stage + t = b"x" + self.ed.SetText(len(t), t) + self.ed.UpperCase() + self.assertEquals(self.ed.Contents(), b"x") + + def testASCII(self): + t = b"x" + self.ed.SetText(len(t), t) + self.ed.SetSel(0,1) + self.ed.UpperCase() + self.assertEquals(self.ed.Contents(), b"X") + + def testLatin1(self): + t = "å".encode("Latin-1") + r = "Å".encode("Latin-1") + self.ed.SetText(len(t), t) + self.ed.SetSel(0,1) + self.ed.UpperCase() + self.assertEquals(self.ed.Contents(), r) + + def testRussian(self): + self.ed.StyleSetCharacterSet(self.ed.STYLE_DEFAULT, self.ed.SC_CHARSET_RUSSIAN) + t = "Б".encode("Windows-1251") + r = "б".encode("Windows-1251") + self.ed.SetText(len(t), t) + self.ed.SetSel(0,1) + self.ed.LowerCase() + self.assertEquals(self.ed.Contents(), r) + + def testUTF(self): + self.ed.SetCodePage(65001) + t = "å".encode("UTF-8") + r = "Å".encode("UTF-8") + self.ed.SetText(len(t), t) + self.ed.SetSel(0,2) + self.ed.UpperCase() + self.assertEquals(self.ed.Contents(), r) + + def testUTFDifferentLength(self): + self.ed.SetCodePage(65001) + t = "ı".encode("UTF-8") + r = "I".encode("UTF-8") + self.ed.SetText(len(t), t) + self.assertEquals(self.ed.Length, 2) + self.ed.SetSel(0,2) + self.ed.UpperCase() + self.assertEquals(self.ed.Length, 1) + self.assertEquals(self.ed.Contents(), r) + +class TestCaseInsensitiveSearch(unittest.TestCase): + def setUp(self): + self.xite = XiteWin.xiteFrame + self.ed = self.xite.ed + self.ed.ClearAll() + self.ed.EmptyUndoBuffer() + + def tearDown(self): + self.ed.SetCodePage(0) + self.ed.StyleSetCharacterSet(self.ed.STYLE_DEFAULT, self.ed.SC_CHARSET_DEFAULT) + + def testEmpty(self): + text = b" x X" + searchString = b"" + self.ed.SetText(len(text), text) + self.ed.TargetStart = 0 + self.ed.TargetEnd = self.ed.Length-1 + self.ed.SearchFlags = 0 + pos = self.ed.SearchInTarget(len(searchString), searchString) + self.assertEquals(0, pos) + + def testASCII(self): + text = b" x X" + searchString = b"X" + self.ed.SetText(len(text), text) + self.ed.TargetStart = 0 + self.ed.TargetEnd = self.ed.Length-1 + self.ed.SearchFlags = 0 + pos = self.ed.SearchInTarget(len(searchString), searchString) + self.assertEquals(1, pos) + + def testLatin1(self): + text = "Frånd Åå".encode("Latin-1") + searchString = "Å".encode("Latin-1") + self.ed.SetText(len(text), text) + self.ed.TargetStart = 0 + self.ed.TargetEnd = self.ed.Length-1 + self.ed.SearchFlags = 0 + pos = self.ed.SearchInTarget(len(searchString), searchString) + self.assertEquals(2, pos) + + def testRussian(self): + self.ed.StyleSetCharacterSet(self.ed.STYLE_DEFAULT, self.ed.SC_CHARSET_RUSSIAN) + text = "=(Б tex б)".encode("Windows-1251") + searchString = "б".encode("Windows-1251") + self.ed.SetText(len(text), text) + self.ed.TargetStart = 0 + self.ed.TargetEnd = self.ed.Length-1 + self.ed.SearchFlags = 0 + pos = self.ed.SearchInTarget(len(searchString), searchString) + self.assertEquals(2, pos) + + def testUTF(self): + self.ed.SetCodePage(65001) + text = "Frånd Åå".encode("UTF-8") + searchString = "Å".encode("UTF-8") + self.ed.SetText(len(text), text) + self.ed.TargetStart = 0 + self.ed.TargetEnd = self.ed.Length-1 + self.ed.SearchFlags = 0 + pos = self.ed.SearchInTarget(len(searchString), searchString) + self.assertEquals(2, pos) + + def testUTFDifferentLength(self): + # Searching for a two byte string "ı" finds a single byte "I" + self.ed.SetCodePage(65001) + text = "Fråndi Ååİ $".encode("UTF-8") + firstPosition = len("Frånd".encode("UTF-8")) + searchString = "İ".encode("UTF-8") + self.assertEquals(len(searchString), 2) + self.ed.SetText(len(text), text) + self.ed.TargetStart = 0 + self.ed.TargetEnd = self.ed.Length-1 + self.ed.SearchFlags = 0 + pos = self.ed.SearchInTarget(len(searchString), searchString) + self.assertEquals(firstPosition, pos) + self.assertEquals(firstPosition+1, self.ed.TargetEnd) + +class TestLexer(unittest.TestCase): + def setUp(self): + self.xite = XiteWin.xiteFrame + self.ed = self.xite.ed + self.ed.ClearAll() + self.ed.EmptyUndoBuffer() + + def testLexerNumber(self): + self.ed.Lexer = self.ed.SCLEX_CPP + self.assertEquals(self.ed.GetLexer(), self.ed.SCLEX_CPP) + + def testLexerName(self): + self.ed.LexerLanguage = b"cpp" + self.assertEquals(self.ed.GetLexer(), self.ed.SCLEX_CPP) + name = b"-" * 100 + length = self.ed.GetLexerLanguage(0, name) + name = name[:length] + self.assertEquals(name, b"cpp") + +class TestAutoComplete(unittest.TestCase): + + def setUp(self): + self.xite = XiteWin.xiteFrame + self.ed = self.xite.ed + self.ed.ClearAll() + self.ed.EmptyUndoBuffer() + # 1 line of 3 characters + t = b"xxx\n" + self.ed.AddText(len(t), t) + + def testDefaults(self): + self.assertEquals(self.ed.AutoCGetSeparator(), ord(' ')) + self.assertEquals(self.ed.AutoCGetMaxHeight(), 5) + self.assertEquals(self.ed.AutoCGetMaxWidth(), 0) + self.assertEquals(self.ed.AutoCGetTypeSeparator(), ord('?')) + self.assertEquals(self.ed.AutoCGetIgnoreCase(), 0) + self.assertEquals(self.ed.AutoCGetAutoHide(), 1) + self.assertEquals(self.ed.AutoCGetDropRestOfWord(), 0) + + def testChangeDefaults(self): + self.ed.AutoCSetSeparator(ord('-')) + self.assertEquals(self.ed.AutoCGetSeparator(), ord('-')) + self.ed.AutoCSetSeparator(ord(' ')) + + self.ed.AutoCSetMaxHeight(100) + self.assertEquals(self.ed.AutoCGetMaxHeight(), 100) + self.ed.AutoCSetMaxHeight(5) + + self.ed.AutoCSetMaxWidth(100) + self.assertEquals(self.ed.AutoCGetMaxWidth(), 100) + self.ed.AutoCSetMaxWidth(0) + + self.ed.AutoCSetTypeSeparator(ord('@')) + self.assertEquals(self.ed.AutoCGetTypeSeparator(), ord('@')) + self.ed.AutoCSetTypeSeparator(ord('?')) + + self.ed.AutoCSetIgnoreCase(1) + self.assertEquals(self.ed.AutoCGetIgnoreCase(), 1) + self.ed.AutoCSetIgnoreCase(0) + + self.ed.AutoCSetAutoHide(0) + self.assertEquals(self.ed.AutoCGetAutoHide(), 0) + self.ed.AutoCSetAutoHide(1) + + self.ed.AutoCSetDropRestOfWord(1) + self.assertEquals(self.ed.AutoCGetDropRestOfWord(), 1) + self.ed.AutoCSetDropRestOfWord(0) + + def testAutoShow(self): + self.assertEquals(self.ed.AutoCActive(), 0) + self.ed.SetSel(0, 0) + + self.ed.AutoCShow(0, b"za defn ghi") + self.assertEquals(self.ed.AutoCActive(), 1) + #~ time.sleep(2) + self.assertEquals(self.ed.AutoCPosStart(), 0) + self.assertEquals(self.ed.AutoCGetCurrent(), 0) + t = b"xxx" + l = self.ed.AutoCGetCurrentText(5, t) + #~ self.assertEquals(l, 3) + self.assertEquals(t, b"za\0") + self.ed.AutoCCancel() + self.assertEquals(self.ed.AutoCActive(), 0) + + def testAutoShowComplete(self): + self.assertEquals(self.ed.AutoCActive(), 0) + self.ed.SetSel(0, 0) + + self.ed.AutoCShow(0, b"za defn ghi") + self.ed.AutoCComplete() + self.assertEquals(self.ed.Contents(), b"zaxxx\n") + + self.assertEquals(self.ed.AutoCActive(), 0) + + def testAutoShowSelect(self): + self.assertEquals(self.ed.AutoCActive(), 0) + self.ed.SetSel(0, 0) + + self.ed.AutoCShow(0, b"za defn ghi") + self.ed.AutoCSelect(0, b"d") + self.ed.AutoCComplete() + self.assertEquals(self.ed.Contents(), b"defnxxx\n") + + self.assertEquals(self.ed.AutoCActive(), 0) + +class TestDirectAccess(unittest.TestCase): + + def setUp(self): + self.xite = XiteWin.xiteFrame + self.ed = self.xite.ed + self.ed.ClearAll() + self.ed.EmptyUndoBuffer() + + # def testGapPosition(self): + # text = b"abcd" + # self.ed.SetText(len(text), text) + # self.assertEquals(self.ed.GapPosition, 4) + # self.ed.TargetStart = 1 + # self.ed.TargetEnd = 1 + # rep = b"-" + # self.ed.ReplaceTarget(len(rep), rep) + # self.assertEquals(self.ed.GapPosition, 2) + + def testCharacterPointerAndRangePointer(self): + text = b"abcd" + self.ed.SetText(len(text), text) + characterPointer = self.ed.CharacterPointer + # rangePointer = self.ed.GetRangePointer(0,3) + # self.assertEquals(characterPointer, rangePointer) + cpBuffer = ctypes.c_char_p(characterPointer) + self.assertEquals(cpBuffer.value, text) + # Gap will not be moved as already moved for CharacterPointer call + # rangePointer = self.ed.GetRangePointer(1,3) + # cpBuffer = ctypes.c_char_p(rangePointer) + # self.assertEquals(cpBuffer.value, text[1:]) + +class TestWordChars(unittest.TestCase): + def setUp(self): + self.xite = XiteWin.xiteFrame + self.ed = self.xite.ed + self.ed.ClearAll() + self.ed.EmptyUndoBuffer() + + def tearDown(self): + self.ed.SetCharsDefault() + + def _setChars(self, charClass, chars): + """ Wrapper to call self.ed.Set*Chars with the right type + @param charClass {str} the character class, "word", "space", etc. + @param chars {iterable of int} characters to set + """ + if sys.version_info.major == 2: + # Python 2, use latin-1 encoded str + unichars = (unichr(x) for x in chars if x != 0) + # can't use literal u"", that's a syntax error in Py3k + # uncode() doesn't exist in Py3k, but we never run it there + result = unicode("").join(unichars).encode("latin-1") + else: + # Python 3, use bytes() + result = bytes(x for x in chars if x != 0) + meth = getattr(self.ed, "Set%sChars" % (charClass.capitalize())) + return meth(None, result) + + def assertCharSetsEqual(self, first, second, *args, **kwargs): + """ Assert that the two character sets are equal. + If either set are an iterable of numbers, convert them to chars + first. """ + first_set = set() + for c in first: + first_set.add(chr(c) if isinstance(c, int) else c) + second_set = set() + for c in second: + second_set.add(chr(c) if isinstance(c, int) else c) + return self.assertEqual(first_set, second_set, *args, **kwargs) + + # def testDefaultWordChars(self): + # # check that the default word chars are as expected + # import string + # dataLen = self.ed.GetWordChars(None, None) + # data = b"\0" * dataLen + # self.ed.GetWordChars(None, data) + # self.assertEquals(dataLen, len(data)) + # expected = set(string.digits + string.ascii_letters + '_') | \ + # set(chr(x) for x in range(0x80, 0x100)) + # self.assertCharSetsEqual(data, expected) + + # def testDefaultWhitespaceChars(self): + # # check that the default whitespace chars are as expected + # import string + # dataLen = self.ed.GetWhitespaceChars(None, None) + # data = b"\0" * dataLen + # self.ed.GetWhitespaceChars(None, data) + # self.assertEquals(dataLen, len(data)) + # expected = (set(chr(x) for x in (range(0, 0x20))) | set(' ')) - \ + # set(['\r', '\n']) + # self.assertCharSetsEqual(data, expected) + + # def testDefaultPunctuationChars(self): + # # check that the default punctuation chars are as expected + # import string + # dataLen = self.ed.GetPunctuationChars(None, None) + # data = b"\0" * dataLen + # self.ed.GetPunctuationChars(None, data) + # self.assertEquals(dataLen, len(data)) + # expected = set(chr(x) for x in range(0x20, 0x80)) - \ + # set(string.ascii_letters + string.digits + "\r\n_ ") + # self.assertCharSetsEqual(data, expected) + + # def testCustomWordChars(self): + # # check that setting things to whitespace chars makes them not words + # self._setChars("whitespace", range(1, 0x100)) + # dataLen = self.ed.GetWordChars(None, None) + # data = b"\0" * dataLen + # self.ed.GetWordChars(None, data) + # self.assertEquals(dataLen, len(data)) + # expected = set() + # self.assertCharSetsEqual(data, expected) + # # and now set something to make sure that works too + # expected = set(range(1, 0x100, 2)) + # self._setChars("word", expected) + # dataLen = self.ed.GetWordChars(None, None) + # data = b"\0" * dataLen + # self.ed.GetWordChars(None, data) + # self.assertEquals(dataLen, len(data)) + # self.assertCharSetsEqual(data, expected) + + # def testCustomWhitespaceChars(self): + # # check setting whitespace chars to non-default values + # self._setChars("word", range(1, 0x100)) + # # we can't change chr(0) from being anything but whitespace + # expected = set([0]) + # dataLen = self.ed.GetWhitespaceChars(None, None) + # data = b"\0" * dataLen + # self.ed.GetWhitespaceChars(None, data) + # self.assertEquals(dataLen, len(data)) + # self.assertCharSetsEqual(data, expected) + # # now try to set it to something custom + # expected = set(range(1, 0x100, 2)) | set([0]) + # self._setChars("whitespace", expected) + # dataLen = self.ed.GetWhitespaceChars(None, None) + # data = b"\0" * dataLen + # self.ed.GetWhitespaceChars(None, data) + # self.assertEquals(dataLen, len(data)) + # self.assertCharSetsEqual(data, expected) + + # def testCustomPunctuationChars(self): + # # check setting punctuation chars to non-default values + # self._setChars("word", range(1, 0x100)) + # expected = set() + # dataLen = self.ed.GetPunctuationChars(None, None) + # data = b"\0" * dataLen + # self.ed.GetPunctuationChars(None, data) + # self.assertEquals(dataLen, len(data)) + # self.assertEquals(set(data), expected) + # # now try to set it to something custom + # expected = set(range(1, 0x100, 1)) + # self._setChars("punctuation", expected) + # dataLen = self.ed.GetPunctuationChars(None, None) + # data = b"\0" * dataLen + # self.ed.GetPunctuationChars(None, data) + # self.assertEquals(dataLen, len(data)) + # self.assertCharSetsEqual(data, expected) + +#~ import os +#~ for x in os.getenv("PATH").split(";"): + #~ n = "scilexer.dll" + #~ nf = x + "\\" + n + #~ print os.access(nf, os.R_OK), nf +if __name__ == '__main__': + uu = XiteWin.main("simpleTests") + #~ for x in sorted(uu.keys()): + #~ print(x, uu[x]) + #~ print() diff --git a/scintilla/test/unit/README b/scintilla/test/unit/README new file mode 100644 index 00000000..0d70fe71 --- /dev/null +++ b/scintilla/test/unit/README @@ -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 diff --git a/scintilla/test/unit/SciTE.properties b/scintilla/test/unit/SciTE.properties new file mode 100644 index 00000000..bf4b5ec7 --- /dev/null +++ b/scintilla/test/unit/SciTE.properties @@ -0,0 +1,4 @@ +if PLAT_WIN + make.command=mingw32-make +command.go.*.cxx=./unitTest +command.go.needs.$(file.patterns.cplusplus)=$(make.command) diff --git a/scintilla/test/unit/makefile b/scintilla/test/unit/makefile new file mode 100644 index 00000000..e5662bc0 --- /dev/null +++ b/scintilla/test/unit/makefile @@ -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 $@ diff --git a/scintilla/test/unit/testContractionState.cxx b/scintilla/test/unit/testContractionState.cxx new file mode 100644 index 00000000..5ba9c407 --- /dev/null +++ b/scintilla/test/unit/testContractionState.cxx @@ -0,0 +1,146 @@ +// Unit Tests for Scintilla internal data structures + +#include + +#include "Platform.h" + +#include "SplitVector.h" +#include "Partitioning.h" +#include "RunStyles.h" +#include "ContractionState.h" + +#include + +// 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)); +} diff --git a/scintilla/test/unit/testPartitioning.cxx b/scintilla/test/unit/testPartitioning.cxx new file mode 100644 index 00000000..1c252ea0 --- /dev/null +++ b/scintilla/test/unit/testPartitioning.cxx @@ -0,0 +1,208 @@ +// Unit Tests for Scintilla internal data structures + +#include + +#include "Platform.h" + +#include "SplitVector.h" +#include "Partitioning.h" + +#include + +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; iLength(); 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 diff --git a/scintilla/test/unit/testRunStyles.cxx b/scintilla/test/unit/testRunStyles.cxx new file mode 100644 index 00000000..db0c3c43 --- /dev/null +++ b/scintilla/test/unit/testRunStyles.cxx @@ -0,0 +1,283 @@ +// Unit Tests for Scintilla internal data structures + +#include + +#include "Platform.h" + +#include "SplitVector.h" +#include "Partitioning.h" +#include "RunStyles.h" + +#include + +// 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()); +} + diff --git a/scintilla/test/unit/testSparseState.cxx b/scintilla/test/unit/testSparseState.cxx new file mode 100644 index 00000000..8a9699db --- /dev/null +++ b/scintilla/test/unit/testSparseState.cxx @@ -0,0 +1,244 @@ +// Unit Tests for Scintilla internal data structures + +#include +#include +#include + +#include "Platform.h" + +#include "SparseState.h" + +#include + +// Test SparseState. + +class SparseStateTest : public ::testing::Test { +protected: + virtual void SetUp() { + pss = new SparseState(); + } + + virtual void TearDown() { + delete pss; + pss = 0; + } + + SparseState *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 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 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 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 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 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 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(); + } + + virtual void TearDown() { + delete pss; + pss = 0; + } + + SparseState *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)); +} diff --git a/scintilla/test/unit/testSplitVector.cxx b/scintilla/test/unit/testSplitVector.cxx new file mode 100644 index 00000000..f5dc1bd7 --- /dev/null +++ b/scintilla/test/unit/testSplitVector.cxx @@ -0,0 +1,220 @@ +// Unit Tests for Scintilla internal data structures + +#include + +#include "Platform.h" + +#include "SplitVector.h" + +#include + +// Test SplitVector. + +class SplitVectorTest : public ::testing::Test { +protected: + virtual void SetUp() { + psv = new SplitVector; + } + + virtual void TearDown() { + delete psv; + psv = 0; + } + + SplitVector *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; iLength(); 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; iLength(); i++) { + EXPECT_EQ(0, psv->ValueAt(i)); + } +} + +TEST_F(SplitVectorTest, EnsureLength) { + psv->EnsureLength(4); + EXPECT_EQ(4, psv->Length()); + for (int i=0; iLength(); 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; iLength(); 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; iLength(); 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; iLength(); 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; iLength(); 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; iLength(); 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; iLength(); 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; iLength(); 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; iSetValueAt(i, i+2); + EXPECT_EQ(testLength, psv->Length()); + for (int i=0; iLength(); i++) { + psv->InsertValue(i, 1, i+9); + psv->Delete(i+1); + } + for (int i=0; iLength(); 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; iSetValueAt(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; iLength(); 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; iLength(); 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; iLength(); 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)); + */ +} diff --git a/scintilla/test/unit/unitTest.cxx b/scintilla/test/unit/unitTest.cxx new file mode 100644 index 00000000..6460ae9d --- /dev/null +++ b/scintilla/test/unit/unitTest.cxx @@ -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 + +#include "Platform.h" + +#include + +// 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(); +} diff --git a/scintilla/test/xite.py b/scintilla/test/xite.py new file mode 100644 index 00000000..56b9c5f9 --- /dev/null +++ b/scintilla/test/xite.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- + +import XiteWin + +if __name__ == "__main__": + XiteWin.main("")