Skip to content

Commit 409a810

Browse files
max65482donho
authored andcommitted
Fix searching Unicode character mismatches with ANSI character '?'
Fix notepad-plus-plus#17125, close notepad-plus-plus#17348
1 parent 8452101 commit 409a810

File tree

5 files changed

+113
-34
lines changed

5 files changed

+113
-34
lines changed

PowerEditor/installer/nativeLang/english.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Translation note:
55
2. All the comments are for explanation, they are not for translation.
66
-->
77
<NotepadPlus>
8-
<Native-Langue name="English" filename="english.xml" version="8.9">
8+
<Native-Langue name="English" filename="english.xml" version="8.9.3">
99
<Menu>
1010
<Main>
1111
<!-- Main Menu Entries -->
@@ -1775,6 +1775,7 @@ Find in all files but exclude all folders log or logs recursively:
17751775
<find-status-scope-all value="in entire file"/>
17761776
<find-status-scope-backward value="from start-of-file to caret"/>
17771777
<find-status-scope-forward value="from caret to end-of-file"/>
1778+
<find-status-replace-invalid-replace-chars value="Replace: can't replace with non-ANSI text in ANSI document"/>
17781779
<find-status-invisible-chars-findWhat value="Invisible characters in pasted &quot;Find what&quot; or &quot;Replace with&quot; content"/>
17791780
<find-status-invisible-chars-findWhat-tip value="Warning: Invisible characters in search (or replace) field.
17801781
The text pasted into this field includes invisible (maybe line-ending) characters. If you proceed without deleting them, they will be included in the search (or replace) text."/>

PowerEditor/installer/nativeLang/english_customizable.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Translation note:
55
2. All the comments are for explanation, they are not for translation.
66
-->
77
<NotepadPlus>
8-
<Native-Langue name="English" filename="english_customizable.xml" version="8.9">
8+
<Native-Langue name="English" filename="english_customizable.xml" version="8.9.3">
99
<Menu>
1010
<Main>
1111
<!-- Main Menu Entries -->
@@ -1775,6 +1775,7 @@ Find in all files but exclude all folders log or logs recursively:
17751775
<find-status-scope-all value="in entire file"/>
17761776
<find-status-scope-backward value="from start-of-file to caret"/>
17771777
<find-status-scope-forward value="from caret to end-of-file"/>
1778+
<find-status-replace-invalid-replace-chars value="Replace: can't replace with non-ANSI text in ANSI document"/>
17781779
<find-status-invisible-chars-findWhat value="Invisible characters in pasted &quot;Find what&quot; or &quot;Replace with&quot; content"/>
17791780
<find-status-invisible-chars-findWhat-tip value="Warning: Invisible characters in search (or replace) field.
17801781
The text pasted into this field includes invisible (maybe line-ending) characters. If you proceed without deleting them, they will be included in the search (or replace) text."/>

PowerEditor/src/Notepad_plus.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1946,6 +1946,7 @@ bool Notepad_plus::replaceInFilelist(std::vector<wstring> & fileNames)
19461946
}
19471947

19481948
bool hasInvalidRegExpr = false;
1949+
19491950
NppGUI& nppGUI = (NppParameters::getInstance()).getNppGUI();
19501951

19511952
for (size_t i = 0, updateOnCount = filesPerPercent; i < filesCount; ++i)
@@ -1978,7 +1979,7 @@ bool Notepad_plus::replaceInFilelist(std::vector<wstring> & fileNames)
19781979
if (nbReplaced == FIND_INVALID_REGULAR_EXPRESSION)
19791980
{
19801981
hasInvalidRegExpr = true;
1981-
break;;
1982+
break;
19821983
}
19831984
else
19841985
{
@@ -2019,10 +2020,10 @@ bool Notepad_plus::replaceInFilelist(std::vector<wstring> & fileNames)
20192020
result = stringReplace(result, L"$INT_REPLACE$", std::to_wstring(nbTotal));
20202021
}
20212022

2022-
if (!hasInvalidRegExpr)
2023-
_findReplaceDlg.setStatusbarMessage(result, FSMessage);
2024-
else
2023+
if (hasInvalidRegExpr)
20252024
_findReplaceDlg.setStatusbarMessageWithRegExprErr(&_invisibleEditView);
2025+
else
2026+
_findReplaceDlg.setStatusbarMessage(result, FSMessage);
20262027

20272028
return true;
20282029
}

PowerEditor/src/ScintillaComponent/FindReplaceDlg.cpp

Lines changed: 99 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2942,6 +2942,32 @@ bool FindReplaceDlg::processFindNext(const wchar_t *txt2find, const FindOption *
29422942
return false;
29432943

29442944
const FindOption *pOptions = options?options:_env;
2945+
NativeLangSpeaker* pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
2946+
2947+
// If txt2find contains non-ANSI characters and document type is ANSI, search cannot match
2948+
if (isSearchUnicodeCharOnAnsi(txt2find))
2949+
{
2950+
if (oFindStatus)
2951+
*oFindStatus = FSNotFound;
2952+
2953+
// Show warning for regex (could work in theory, but we disallow it to prevent unexpected behavior)
2954+
if (pOptions->_incrementalType == NotIncremental) //incremental search doesn't trigger messages
2955+
{
2956+
setStatusMessageNotFound(txt2find);
2957+
2958+
// if the dialog is not shown, pass the focus to his parent(ie. Notepad++)
2959+
if (!::IsWindowVisible(_hSelf))
2960+
{
2961+
(*_ppEditView)->grabFocus();
2962+
}
2963+
else
2964+
{
2965+
::SetFocus(::GetDlgItem(_hSelf, IDFINDWHAT));
2966+
}
2967+
}
2968+
return false;
2969+
2970+
}
29452971

29462972
(*_ppEditView)->execute(SCI_CALLTIPCANCEL);
29472973

@@ -3025,8 +3051,6 @@ bool FindReplaceDlg::processFindNext(const wchar_t *txt2find, const FindOption *
30253051

30263052
(*_ppEditView)->execute(SCI_SETSEARCHFLAGS, flags);
30273053

3028-
NativeLangSpeaker* pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
3029-
30303054
posFind = (*_ppEditView)->searchInTarget(pText, stringSizeFind, startPosition, endPosition);
30313055
if (posFind == -1) //no match found in target, check if a new target should be used
30323056
{
@@ -3059,27 +3083,13 @@ bool FindReplaceDlg::processFindNext(const wchar_t *txt2find, const FindOption *
30593083
//failed, or failed twice with wrap
30603084
if (pOptions->_incrementalType == NotIncremental) //incremental search doesn't trigger messages
30613085
{
3062-
wstring warningMsg = pNativeSpeaker->getLocalizedStrFromID("find-status-cannot-find", L"Find: Can't find the text \"$STR_REPLACE$\"");
3063-
wstring newTxt2find = stringReplace(txt2find, L"&", L"&&");
3064-
3065-
if (newTxt2find.length() > 32) // truncate the search string to display, if the search string is too long
3066-
{
3067-
newTxt2find.erase(28);
3068-
newTxt2find += L"...";
3069-
}
3070-
3071-
warningMsg = stringReplace(warningMsg, L"$STR_REPLACE$", newTxt2find);
3072-
3073-
warningMsg += L" ";
3074-
warningMsg += getScopeInfoForStatusBar(&_options);
3075-
30763086
wstring reasonMsg;
30773087
bool isTheMostLaxMode = _options._isWrapAround && !_options._isMatchCase && !_options._isWholeWord;
30783088
if (!isTheMostLaxMode)
30793089
{
30803090
reasonMsg = pNativeSpeaker->getLocalizedStrFromID("find-status-cannot-find-pebkac-maybe", noFoundPotentialReason);
30813091
}
3082-
setStatusbarMessage(warningMsg, FSNotFound, reasonMsg);
3092+
setStatusMessageNotFound(txt2find, reasonMsg);
30833093

30843094
// if the dialog is not shown, pass the focus to his parent(ie. Notepad++)
30853095
if (!::IsWindowVisible(_hSelf))
@@ -3136,9 +3146,10 @@ bool FindReplaceDlg::processReplace(const wchar_t *txt2find, const wchar_t *txt2
31363146
if (!txt2find || !txt2find[0] || !txt2replace)
31373147
return false;
31383148

3149+
NativeLangSpeaker* pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
3150+
31393151
if ((*_ppEditView)->getCurrentBuffer()->isReadOnly())
31403152
{
3141-
NativeLangSpeaker *pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
31423153
wstring msg = pNativeSpeaker->getLocalizedStrFromID("find-status-replace-readonly", L"Replace: Cannot replace text. The current document is read only.");
31433154
setStatusbarMessage(msg, FSNotFound);
31443155
return false;
@@ -3147,12 +3158,30 @@ bool FindReplaceDlg::processReplace(const wchar_t *txt2find, const wchar_t *txt2
31473158
FindOption replaceOptions = options ? *options : *_env;
31483159
replaceOptions._incrementalType = FirstIncremental;
31493160

3161+
// If txt2find contains non-ANSI characters and document type is ANSI, search cannot match
3162+
if (isSearchUnicodeCharOnAnsi(txt2find))
3163+
{
3164+
wstring msg = pNativeSpeaker->getLocalizedStrFromID("find-status-replace-not-found", L"Replace: no occurrence was found");
3165+
3166+
msg += L" ";
3167+
msg += getScopeInfoForStatusBar(&_options);
3168+
3169+
setStatusbarMessage(msg, FSNotFound);
3170+
3171+
return false;
3172+
}
3173+
else if (isSearchUnicodeCharOnAnsi(txt2replace))
3174+
{
3175+
NativeLangSpeaker* pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
3176+
wstring msg = pNativeSpeaker->getLocalizedStrFromID("find-status-replace-invalid-replace-chars", L"Replace: can't replace with non-ANSI text in ANSI document");
3177+
setStatusbarMessage(msg, FSNotFound);
3178+
return false;
3179+
}
3180+
31503181
Sci_CharacterRangeFull currentSelection = (*_ppEditView)->getSelection();
31513182
FindStatus status;
31523183
moreMatches = processFindNext(txt2find, &replaceOptions, &status, FINDNEXTTYPE_FINDNEXTFORREPLACE);
31533184

3154-
NativeLangSpeaker* pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
3155-
31563185
if (moreMatches)
31573186
{
31583187
Sci_CharacterRangeFull nextFind = (*_ppEditView)->getSelection();
@@ -3269,24 +3298,35 @@ int FindReplaceDlg::markAllInc(const FindOption *opt)
32693298

32703299
int FindReplaceDlg::processAll(ProcessOperation op, const FindOption *opt, bool isEntire, const FindersInfo *pFindersInfo, int colourStyleID)
32713300
{
3301+
NativeLangSpeaker* pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
32723302
if (op == ProcessReplaceAll && (*_ppEditView)->getCurrentBuffer()->isReadOnly())
32733303
{
3274-
NppParameters& nppParam = NppParameters::getInstance();
3275-
NativeLangSpeaker *pNativeSpeaker = nppParam.getNativeLangSpeaker();
32763304
wstring msg = pNativeSpeaker->getLocalizedStrFromID("find-status-replaceall-readonly", L"Replace All: Cannot replace text. The current document is read only.");
32773305
setStatusbarMessage(msg, FSNotFound);
32783306
return 0;
32793307
}
32803308

3281-
// Turn OFF all the notification of modification (SCN_MODIFIED) for the sake of performance
3282-
LRESULT notifFlag = (*_ppEditView)->execute(SCI_GETMODEVENTMASK);
3283-
(*_ppEditView)->execute(SCI_SETMODEVENTMASK, 0);
3309+
const FindOption* pOptions = opt ? opt : _env;
3310+
const wchar_t* txt2find = pOptions->_str2Search.c_str();
3311+
const wchar_t* txt2replace = pOptions->_str4Replace.c_str();
32843312

3313+
// If txt2find contains non-ANSI characters and document type is ANSI, search cannot match
3314+
if (isSearchUnicodeCharOnAnsi(txt2find))
3315+
{
3316+
return 0;
3317+
}
32853318

3319+
// If txt2replace contains non-ANSI characters and document type is ANSI, the match cannot be replaced
3320+
if (isSearchUnicodeCharOnAnsi(txt2replace))
3321+
{
3322+
wstring msg = pNativeSpeaker->getLocalizedStrFromID("find-status-replace-invalid-replace-chars", L"Replace: can't replace with non-ANSI text in ANSI document");
3323+
setStatusbarMessage(msg, FSNotFound);
3324+
return 0;
3325+
}
32863326

3287-
const FindOption *pOptions = opt?opt:_env;
3288-
const wchar_t *txt2find = pOptions->_str2Search.c_str();
3289-
const wchar_t *txt2replace = pOptions->_str4Replace.c_str();
3327+
// Turn OFF all the notification of modification (SCN_MODIFIED) for the sake of performance
3328+
LRESULT notifFlag = (*_ppEditView)->execute(SCI_GETMODEVENTMASK);
3329+
(*_ppEditView)->execute(SCI_SETMODEVENTMASK, 0);
32903330

32913331
Sci_CharacterRangeFull cr = (*_ppEditView)->getSelection();
32923332
size_t docLength = (*_ppEditView)->execute(SCI_GETLENGTH);
@@ -4381,6 +4421,26 @@ void FindReplaceDlg::setStatusbarMessageWithRegExprErr(ScintillaEditView* pEditV
43814421
setStatusbarMessage(result, FSNotFound, string2wstring(s, CP_UTF8));
43824422
}
43834423

4424+
void FindReplaceDlg::setStatusMessageNotFound(const std::wstring& textNotFound, const std::wstring& tooltipMsg)
4425+
{
4426+
NativeLangSpeaker* pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
4427+
wstring warningMsg = pNativeSpeaker->getLocalizedStrFromID("find-status-cannot-find", L"Find: Can't find the text \"$STR_REPLACE$\"");
4428+
wstring newTxt2find = stringReplace(textNotFound, L"&", L"&&");
4429+
4430+
if (newTxt2find.length() > 32) // truncate the search string to display, if the search string is too long
4431+
{
4432+
newTxt2find.erase(28);
4433+
newTxt2find += L"...";
4434+
}
4435+
4436+
warningMsg = stringReplace(warningMsg, L"$STR_REPLACE$", newTxt2find);
4437+
4438+
warningMsg += L" ";
4439+
warningMsg += getScopeInfoForStatusBar(&_options);
4440+
4441+
setStatusbarMessage(warningMsg, FSNotFound, tooltipMsg);
4442+
}
4443+
43844444
void FindReplaceDlg::setStatusMessageWithInvisibleCharsWarning()
43854445
{
43864446
NativeLangSpeaker* pNativeSpeaker = (NppParameters::getInstance()).getNativeLangSpeaker();
@@ -5513,6 +5573,17 @@ bool FindReplaceDlg::replaceInOpenDocsConfirmCheck()
55135573
return confirmed;
55145574
}
55155575

5576+
bool FindReplaceDlg::isSearchUnicodeCharOnAnsi(const wchar_t* text) const
5577+
{
5578+
if ((*_ppEditView)->execute(SCI_GETCODEPAGE) == CP_ACP)
5579+
{
5580+
BOOL convertible = FALSE;
5581+
WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, text, -1, nullptr, 0, nullptr, &convertible);
5582+
return convertible;
5583+
}
5584+
return false;
5585+
}
5586+
55165587
// Expand selection (if needed) and set the selected text in Find What field.
55175588
// Return empty string if nothing to set in find field.
55185589
// Otherwise return string in which the selected text was copied.

PowerEditor/src/ScintillaComponent/FindReplaceDlg.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,8 +406,11 @@ public :
406406
void clearMarks(const FindOption& opt);
407407
void setStatusbarMessage(const std::wstring & msg, FindStatus status, const std::wstring& tooltipMsg = L"");
408408
void setStatusbarMessageWithRegExprErr(ScintillaEditView* pEditView);
409+
void setStatusMessageNotFound(const std::wstring& textNotFound, const std::wstring& tooltipMsg = L"");
410+
409411
void setStatusMessageWithInvisibleCharsWarning();
410412
void removeStatusMessageWithInvisibleCharsWarning();
413+
411414
std::wstring getScopeInfoForStatusBar(FindOption const *pFindOpt) const;
412415
Finder * createFinder();
413416
bool removeFinder(Finder *finder2remove);
@@ -522,6 +525,8 @@ protected :
522525
bool replaceInProjectsConfirmCheck();
523526
bool replaceInOpenDocsConfirmCheck();
524527

528+
bool isSearchUnicodeCharOnAnsi(const wchar_t* text) const;
529+
525530
ContextMenu _swapPopupMenu;
526531
enum SwapButtonStatus {swap, down, up} _swapButtonStatus = swap;
527532
HWND _hSwapButton = nullptr;

0 commit comments

Comments
 (0)