From c9d7ca687c6eb00063ee4bdf7d4c67a321e20b31 Mon Sep 17 00:00:00 2001 From: Rainer Kottenhoff Date: Wed, 10 Jan 2018 10:08:42 +0100 Subject: [PATCH] + fix: protect SCI target transactions by a guard to break recursion of Mark Occurrences caused by ChangeNotification() events --- src/Edit.c | 118 +++++++++++++++++++++++++++++++++++++++++++------ src/Edit.h | 5 +++ src/Notepad3.c | 80 +++++---------------------------- src/SciCall.h | 1 + 4 files changed, 122 insertions(+), 82 deletions(-) diff --git a/src/Edit.c b/src/Edit.c index 15e844874..0a2a5108a 100644 --- a/src/Edit.c +++ b/src/Edit.c @@ -111,15 +111,6 @@ static char PunctuationCharsAccelerated[1] = { '\0' }; // empty! //static WCHAR W_WhiteSpaceCharsDefault[DELIM_BUFFER] = { L'\0' }; //static WCHAR W_WhiteSpaceCharsAccelerated[DELIM_BUFFER] = { L'\0' }; - -// Timer bitfield -static volatile LONG g_lEditTimerBits = 0; -#define TIMER_BIT_MARK_OCC 1L -//#define TIMER_BIT_ONOTHER_TIMER 2L -#define TEST_AND_SET(B) InterlockedBitTestAndSet(&g_lEditTimerBits, B) -#define TEST_AND_RESET(B) InterlockedBitTestAndReset(&g_lEditTimerBits, B) - - enum AlignMask { ALIGN_LEFT = 0, ALIGN_RIGHT = 1, @@ -144,6 +135,30 @@ enum SortOrderMask { extern LPMRULIST mruFind; extern LPMRULIST mruReplace; +extern BOOL bMarkOccurrencesCurrentWord; +extern BOOL bMarkOccurrencesMatchCase; +extern BOOL bMarkOccurrencesMatchWords; + +// Timer bitfield +static volatile LONG g_lTargetTransactionBits = 0; +#define TIMER_BIT_MARK_OCC 1L +#define BLOCK_BIT_TARGET_TRANSACTION 2L +#define TEST_AND_SET(B) InterlockedBitTestAndSet(&g_lTargetTransactionBits, B) +#define TEST_AND_RESET(B) InterlockedBitTestAndReset(&g_lTargetTransactionBits, B) + + +//============================================================================= +// +// EditEnterTargetTransaction(), EditLeaveTargetTransaction() +// +BOOL EditEnterTargetTransaction() { + return (BOOL)TEST_AND_SET(BLOCK_BIT_TARGET_TRANSACTION); +} + +BOOL EditLeaveTargetTransaction() { + return (BOOL)TEST_AND_RESET(BLOCK_BIT_TARGET_TRANSACTION); +} + //============================================================================= // @@ -4248,6 +4263,8 @@ int __fastcall EditFindInTarget(HWND hwnd, LPCSTR szFind, int length, int flags, int _end = *end; BOOL bFindPrev = (_start > _end); + EditEnterTargetTransaction(); + SendMessage(hwnd, SCI_SETSEARCHFLAGS, flags, 0); SendMessage(hwnd, SCI_SETTARGETRANGE, _start, _end); int iPos = (int)SendMessage(hwnd, SCI_SEARCHINTARGET, length, (LPARAM)szFind); @@ -4274,6 +4291,9 @@ int __fastcall EditFindInTarget(HWND hwnd, LPCSTR szFind, int length, int flags, *start = (int)SendMessage(hwnd, SCI_GETTARGETSTART, 0, 0); *end = (int)SendMessage(hwnd, SCI_GETTARGETEND, 0, 0); } + + EditLeaveTargetTransaction(); + return iPos; } @@ -5244,6 +5264,68 @@ BOOL EditFindPrev(HWND hwnd, LPCEDITFINDREPLACE lpefr, BOOL bExtendSelection) { } + +//============================================================================= +// +// EditMarkAllOccurrences() +// +void EditMarkAllOccurrences() +{ + if (iMarkOccurrences != 0) { + + if (EditEnterTargetTransaction()) { return; } // do not block, next event occurs for sure + + if (bMarkOccurrencesMatchVisible) + { + // get visible lines for update + int iFirstVisibleLine = SciCall_DocLineFromVisible(SciCall_GetFirstVisibleLine()); + + int iStartLine = max(0, (iFirstVisibleLine - SciCall_LinesOnScreen())); + int iEndLine = min((iFirstVisibleLine + (SciCall_LinesOnScreen() << 1)), (SciCall_GetLineCount() - 1)); + + int iPosStart = SciCall_PositionFromLine(iStartLine); + int iPosEnd = SciCall_GetLineEndPosition(iEndLine); + + // !!! don't clear all marks, else this method is re-called + // !!! on UpdateUI notification on drawing indicator mark + EditMarkAll(g_hwndEdit, NULL, bMarkOccurrencesCurrentWord, iPosStart, iPosEnd, bMarkOccurrencesMatchCase, bMarkOccurrencesMatchWords); + } + else { + EditMarkAll(g_hwndEdit, NULL, bMarkOccurrencesCurrentWord, 0, SciCall_GetTextLength(), bMarkOccurrencesMatchCase, bMarkOccurrencesMatchWords); + UpdateStatusbar(); + } + + EditLeaveTargetTransaction(); + } +} + + +//============================================================================= +// +// EditUpdateVisibleUrlHotspotr() +// +void EditUpdateVisibleUrlHotspot() +{ + if (bHyperlinkHotspot) + { + if (EditEnterTargetTransaction()) { return; } // do not block, next event occurs for sure + + // get visible lines for update + int iFirstVisibleLine = SciCall_DocLineFromVisible(SciCall_GetFirstVisibleLine()); + + int iStartLine = max(0, (iFirstVisibleLine - SciCall_LinesOnScreen())); + int iEndLine = min((iFirstVisibleLine + (SciCall_LinesOnScreen() << 1)), (SciCall_GetLineCount() - 1)); + + int iPosStart = SciCall_PositionFromLine(iStartLine); + int iPosEnd = SciCall_GetLineEndPosition(iEndLine); + + EditUpdateUrlHotspots(g_hwndEdit, iPosStart, iPosEnd, bHyperlinkHotspot); + + EditLeaveTargetTransaction(); + } +} + + //============================================================================= // // EditGetReplaceString() @@ -5302,15 +5384,19 @@ BOOL EditReplace(HWND hwnd, LPCEDITFINDREPLACE lpefr) { } } - SendMessage(hwnd, SCI_TARGETFROMSELECTION, 0, 0); + EditEnterTargetTransaction(); + + SciCall_TargetFromSelection(); SendMessage(hwnd, iReplaceMsg, (WPARAM)-1, (LPARAM)pszReplace); - - LocalFree(pszReplace); - + // move caret behind replacement int after = (int)SendMessage(hwnd, SCI_GETTARGETEND, 0, 0); SendMessage(hwnd, SCI_SETSEL, after, after); + EditLeaveTargetTransaction(); + + LocalFree(pszReplace); + return EditFindNext(hwnd, lpefr, FALSE); } @@ -5393,6 +5479,7 @@ int EditReplaceAllInRange(HWND hwnd, LPCEDITFINDREPLACE lpefr, BOOL bShowInfo, i // === iterate over findings and replace strings === + int offset = 0; for (ReplPos_t* pPosPair = (ReplPos_t*)utarray_front(ReplPosUTArray); pPosPair != NULL; @@ -5402,6 +5489,9 @@ int EditReplaceAllInRange(HWND hwnd, LPCEDITFINDREPLACE lpefr, BOOL bShowInfo, i start = pPosPair->beg + offset; end = iEndPos + offset; iPos = EditFindInTarget(hwnd, szFind, slen, (int)(lpefr->fuFlags), &start, &end, FALSE); + + EditEnterTargetTransaction(); + // @@@ found same ? //if ((iPos >= 0) && (start == (pPosPair->beg + offset)) && (end == (pPosPair->end + offset))) { SciCall_SetTargetRange(start, end); @@ -5410,6 +5500,8 @@ int EditReplaceAllInRange(HWND hwnd, LPCEDITFINDREPLACE lpefr, BOOL bShowInfo, i //else { // // this should not happen !!! //} + + EditLeaveTargetTransaction(); } EndWaitCursor(); diff --git a/src/Edit.h b/src/Edit.h index d6445a704..5e2cfb632 100644 --- a/src/Edit.h +++ b/src/Edit.h @@ -132,6 +132,11 @@ void EditSetBookmarkList(HWND,LPCWSTR); void EditApplyLexerStyle(HWND, int, int); void EditFinalizeStyling(HWND); +void EditMarkAllOccurrences(); +void EditUpdateVisibleUrlHotspot(); + +BOOL EditEnterTargetTransaction(); +BOOL EditLeaveTargetTransaction(); //void SciInitThemes(HWND); //LRESULT CALLBACK SciThemedWndProc(HWND,UINT,WPARAM,LPARAM); diff --git a/src/Notepad3.c b/src/Notepad3.c index c954043ef..da8a779e8 100644 --- a/src/Notepad3.c +++ b/src/Notepad3.c @@ -322,7 +322,6 @@ static DWORD DropFilesProc(CLIPFORMAT cf, HGLOBAL hData, HWND hWnd, DWORD dwKeyS static volatile LONG g_lTimerBits = 0; #define TIMER_BIT_MARK_OCC 1L #define TIMER_BIT_UPDATE_HYPER 2L -#define BLOCK_BIT_MARK_OCC 4L #define TEST_AND_SET(B) InterlockedBitTestAndSet(&g_lTimerBits, B) #define TEST_AND_RESET(B) InterlockedBitTestAndReset(&g_lTimerBits, B) @@ -2539,62 +2538,6 @@ LRESULT MsgSysCommand(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam) } - -//============================================================================= -// -// MarkAllOccurrencesTimer() -// -void __fastcall MarkAllOccurrencesTimer() -{ - if (iMarkOccurrences != 0) { - if (!TEST_AND_SET(BLOCK_BIT_MARK_OCC)) { - TEST_AND_RESET(BLOCK_BIT_MARK_OCC); - if (bMarkOccurrencesMatchVisible) - { - // get visible lines for update - int iFirstVisibleLine = SciCall_DocLineFromVisible(SciCall_GetFirstVisibleLine()); - - int iStartLine = max(0, (iFirstVisibleLine - SciCall_LinesOnScreen())); - int iEndLine = min((iFirstVisibleLine + (SciCall_LinesOnScreen() << 1)), (SciCall_GetLineCount() - 1)); - - int iPosStart = SciCall_PositionFromLine(iStartLine); - int iPosEnd = SciCall_GetLineEndPosition(iEndLine); - - // !!! don't clear all marks, else this method is re-called - // !!! on UpdateUI notification on drawing indicator mark - EditMarkAll(g_hwndEdit, NULL, bMarkOccurrencesCurrentWord, iPosStart, iPosEnd, bMarkOccurrencesMatchCase, bMarkOccurrencesMatchWords); - } - else { - EditMarkAll(g_hwndEdit, NULL, bMarkOccurrencesCurrentWord, 0, SciCall_GetTextLength(), bMarkOccurrencesMatchCase, bMarkOccurrencesMatchWords); - UpdateStatusbar(); - } - } - } -} - - -//============================================================================= -// -// UpdateVisibleUrlHotspotTimer() -// -void __fastcall UpdateVisibleUrlHotspotTimer() -{ - if (bHyperlinkHotspot) - { - // get visible lines for update - int iFirstVisibleLine = SciCall_DocLineFromVisible(SciCall_GetFirstVisibleLine()); - - int iStartLine = max(0, (iFirstVisibleLine - SciCall_LinesOnScreen())); - int iEndLine = min((iFirstVisibleLine + (SciCall_LinesOnScreen() << 1)), (SciCall_GetLineCount() - 1)); - - int iPosStart = SciCall_PositionFromLine(iStartLine); - int iPosEnd = SciCall_GetLineEndPosition(iEndLine); - - EditUpdateUrlHotspots(g_hwndEdit, iPosStart, iPosEnd, bHyperlinkHotspot); - } -} - - //============================================================================= // // MsgCommand() - Handles WM_COMMAND @@ -2607,11 +2550,11 @@ LRESULT MsgCommand(HWND hwnd, WPARAM wParam, LPARAM lParam) switch(LOWORD(wParam)) { case IDC_MAIN_MARKALL_OCC: - MarkAllOccurrencesTimer(); + EditMarkAllOccurrences(); break; case IDC_CALL_UPDATE_HOTSPOT: - UpdateVisibleUrlHotspotTimer(); + EditUpdateVisibleUrlHotspot(); break; case IDM_FILE_NEW: @@ -3491,10 +3434,10 @@ LRESULT MsgCommand(HWND hwnd, WPARAM wParam, LPARAM lParam) { BeginWaitCursor(NULL); int token = BeginSelUndoAction(); - TEST_AND_SET(BLOCK_BIT_MARK_OCC); - SendMessage(g_hwndEdit,SCI_TARGETFROMSELECTION,0,0); + EditEnterTargetTransaction(); + SciCall_TargetFromSelection(); SendMessage(g_hwndEdit,SCI_LINESSPLIT,0,0); - TEST_AND_RESET(BLOCK_BIT_MARK_OCC); + EditLeaveTargetTransaction(); EndSelUndoAction(token); EndWaitCursor(); } @@ -3505,11 +3448,11 @@ LRESULT MsgCommand(HWND hwnd, WPARAM wParam, LPARAM lParam) { BeginWaitCursor(NULL); int token = BeginSelUndoAction(); - TEST_AND_SET(BLOCK_BIT_MARK_OCC); - SendMessage(g_hwndEdit,SCI_TARGETFROMSELECTION,0,0); + EditEnterTargetTransaction(); + SciCall_TargetFromSelection(); SendMessage(g_hwndEdit,SCI_LINESJOIN,0,0); - TEST_AND_RESET(BLOCK_BIT_MARK_OCC); - EditJoinLinesEx(g_hwndEdit); + EditLeaveTargetTransaction(); + EditJoinLinesEx(g_hwndEdit); // needed to join paragraphs ??? EndSelUndoAction(token); EndWaitCursor(); } @@ -7180,7 +7123,7 @@ int CreateIniFileEx(LPCWSTR lpszIniFile) { void MarkAllOccurrences(int delay) { if (delay <= 0) { - MarkAllOccurrencesTimer(); + EditMarkAllOccurrences(); return; } TEST_AND_SET(TIMER_BIT_MARK_OCC); @@ -7194,10 +7137,9 @@ void MarkAllOccurrences(int delay) void UpdateVisibleUrlHotspot(int delay) { if (delay <= 0) { - MarkAllOccurrencesTimer(); + EditUpdateVisibleUrlHotspot(); return; } - TEST_AND_SET(TIMER_BIT_UPDATE_HYPER); SetTimer(g_hwndMain, IDT_TIMER_UPDATE_HOTSPOT, 100, NULL); } diff --git a/src/SciCall.h b/src/SciCall.h index d3dc4f698..ca6dd751d 100644 --- a/src/SciCall.h +++ b/src/SciCall.h @@ -93,6 +93,7 @@ DeclareSciCallR1(GetLineSelEndPosition, GETLINESELENDPOSITION, int, Sci_Position DeclareSciCallV2(SetSel, SETSEL, int, anchorPos, int, currentPos); DeclareSciCallV2(SetTargetRange, SETTARGETRANGE, int, start, int, end); +DeclareSciCallV0(TargetFromSelection, TARGETFROMSELECTION); DeclareSciCallV1(GotoPos, GOTOPOS, int, position); DeclareSciCallV1(GotoLine, GOTOLINE, int, line);