From 5ddfbc08eefb0d2e42fc8bcebdb7e3f3387e6636 Mon Sep 17 00:00:00 2001 From: Rainer Kottenhoff Date: Fri, 30 Aug 2019 14:32:37 +0200 Subject: [PATCH] + fix: Hyperlink detection for Unicode + fix: Insertion handling: keep selection if non empty selection is replaced --- Versions/build.txt | 2 +- minipath/src/Helpers.c | 16 ++++--- res/Notepad3.exe.manifest.conf | 2 +- src/Edit.c | 34 +++++++------- src/Edit.h | 2 +- src/Notepad3.c | 44 ++++++++++++------- src/TypeDefs.h | 6 +++ src/VersionEx.h | 4 +- .../StyleLexers/styleLexTOML/TOML.toml | 5 +++ 9 files changed, 71 insertions(+), 44 deletions(-) diff --git a/Versions/build.txt b/Versions/build.txt index d930c7079..368a8e0c3 100644 --- a/Versions/build.txt +++ b/Versions/build.txt @@ -1 +1 @@ -2607 +2609 diff --git a/minipath/src/Helpers.c b/minipath/src/Helpers.c index fe20433d3..5bdf53dcc 100644 --- a/minipath/src/Helpers.c +++ b/minipath/src/Helpers.c @@ -12,12 +12,18 @@ * * * * *******************************************************************************/ -#if !defined(_WIN32_WINNT) -#define _WIN32_WINNT 0x601 +#if !defined(WINVER) +#define WINVER 0x601 /*_WIN32_WINNT_WIN7*/ #endif -#define VC_EXTRALEAN 1 -//#define WIN32_LEAN_AND_MEAN 1 -//#define NOMINMAX 1 +#if !defined(_WIN32_WINNT) +#define _WIN32_WINNT 0x601 /*_WIN32_WINNT_WIN7*/ +#endif +#if !defined(NTDDI_VERSION) +#define NTDDI_VERSION 0x06010000 /*NTDDI_WIN7*/ +#endif +//~#define VC_EXTRALEAN 1 +//~#define WIN32_LEAN_AND_MEAN 1 +#define NOMINMAX 1 #include #include #include diff --git a/res/Notepad3.exe.manifest.conf b/res/Notepad3.exe.manifest.conf index a6ae68c34..36e50aaa5 100644 --- a/res/Notepad3.exe.manifest.conf +++ b/res/Notepad3.exe.manifest.conf @@ -3,7 +3,7 @@ Notepad3 BETA diff --git a/src/Edit.c b/src/Edit.c index ac6cf4e84..edb85e559 100644 --- a/src/Edit.c +++ b/src/Edit.c @@ -204,12 +204,13 @@ static void CALLBACK MQ_ExecuteNext(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWOR // // EditReplaceSelection() // -void EditReplaceSelection(const char* text, bool bReselect) +void EditReplaceSelection(const char* text, bool bForceSel) { _BEGIN_UNDO_ACTION_ + bool const bSelWasEmpty = SciCall_IsSelectionEmpty(); DocPos const posSelBeg = SciCall_GetSelectionStart(); SciCall_ReplaceSel(text); - if (bReselect) { + if (bForceSel || !bSelWasEmpty) { SciCall_SetSel(posSelBeg, SciCall_GetCurrentPos()); } _END_UNDO_ACTION_ @@ -1754,30 +1755,26 @@ void EditURLEncode(HWND hwnd) const char* pszText = (const char*)SciCall_GetRangePointer(min_p(iCurPos, iAnchorPos), iSelSize); - LPWSTR pszTextW = AllocMem(iSelSize * sizeof(WCHAR), HEAP_ZERO_MEMORY); - if (pszTextW == NULL) { - return; - } - - /*int cchTextW =*/ MultiByteToWideChar(Encoding_SciCP, 0, pszText, (MBWC_DocPos_Cast)(iSelSize-1), - pszTextW, (MBWC_DocPos_Cast)iSelSize); + WCHAR szTextW[INTERNET_MAX_URL_LENGTH+1]; + int const cchTextW = MultiByteToWideChar(Encoding_SciCP, 0, pszText, (MBWC_DocPos_Cast)(iSelSize-1), szTextW, INTERNET_MAX_URL_LENGTH); + szTextW[cchTextW] = L'\0'; size_t const cchEscaped = iSelSize * 3; char* pszEscaped = (char*)AllocMem(cchEscaped, HEAP_ZERO_MEMORY); if (pszEscaped == NULL) { - FreeMem(pszTextW); return; } LPWSTR pszEscapedW = (LPWSTR)AllocMem(cchEscaped * sizeof(WCHAR), HEAP_ZERO_MEMORY); if (pszEscapedW == NULL) { - FreeMem(pszTextW); FreeMem(pszEscaped); return; } DWORD cchEscapedW = (DWORD)cchEscaped; - UrlEscape(pszTextW, pszEscapedW, &cchEscapedW, URL_ESCAPE_SEGMENT_ONLY | URL_ESCAPE_PERCENT | URL_ESCAPE_AS_UTF8); + DWORD const flags = (DWORD)(URL_ESCAPE_SEGMENT_ONLY | URL_ESCAPE_PERCENT | URL_ESCAPE_AS_UTF8); + + UrlEscape(szTextW, pszEscapedW, &cchEscapedW, flags); DWORD const cchEscapedEnc = WideCharToMultiByte(Encoding_SciCP, 0, pszEscapedW, cchEscapedW, pszEscaped, (MBWC_DocPos_Cast)cchEscaped, NULL, NULL); @@ -1807,7 +1804,6 @@ void EditURLEncode(HWND hwnd) _END_UNDO_ACTION_; - FreeMem(pszTextW); FreeMem(pszEscaped); FreeMem(pszEscapedW); } @@ -2215,7 +2211,7 @@ void EditModifyNumber(HWND hwnd,bool bIncrease) { StringCchPrintfA(chFormat, COUNTOF(chFormat), "%%#0%ix", iWidth); StringCchPrintfA(chNumber, COUNTOF(chNumber), chFormat, iNumber); - EditReplaceSelection(chNumber,false); + EditReplaceSelection(chNumber, false); } } } @@ -7151,9 +7147,13 @@ void EditUpdateIndicators(HWND hwnd, DocPos startPos, DocPos endPos, bool bClear if (Settings.HyperlinkHotspot) { - static const char* pUrlRegEx = "\\b(?:(?:https?|ftp|file)://|www\\.|ftp\\.)" - "(?:\\([-A-Z0-9+&@#/%=~_|$?!:,.]*\\)|[-A-Z0-9+&@#/%=~_|$?!:,.])*" - "(?:\\([-A-Z0-9+&@#/%=~_|$?!:,.]*\\)|[A-Z0-9+&@#/%=~_|$])"; + //static const char* pUrlRegEx = "\\b(?:(?:https?|ftp|file)://|www\\.|ftp\\.)" + // "(?:\\([-a-z\\u00a1-\\uffff0-9+&@#/%=~_|$?!:,.]*\\)|[-a-z\\u00a1-\\uffff0-9+&@#/%=~_|$?!:,.])*" + // "(?:\\([-a-z\\u00a1-\\uffff0-9+&@#/%=~_|$?!:,.]*\\)|[a-z\\u00a1-\\uffff0-9+&@#/%=~_|$])"; + + // https://mathiasbynens.be/demo/url-regex : @stephenhay + static const char* pUrlRegEx = "\\b(?:(?:https?|ftp|file)://|www\\.|ftp\\.)[^\\s/$.?#].[^\\s]*"; + _UpdateIndicators(hwnd, INDIC_NP3_HYPERLINK, INDIC_NP3_HYPERLINK_U, pUrlRegEx, startPos, endPos); } diff --git a/src/Edit.h b/src/Edit.h index 9eb979965..dff89681d 100644 --- a/src/Edit.h +++ b/src/Edit.h @@ -21,7 +21,7 @@ #include "TypeDefs.h" void EditInitializeSciCtrl(HWND); -void EditReplaceSelection(const char* text, bool bReselect); +void EditReplaceSelection(const char* text, bool bForceSel); void EditInitWordDelimiter(HWND hwnd); void EditSetNewText(HWND hwnd,const char* lpstrText,DocPos lenText,bool); bool EditConvertText(HWND hwnd, cpi_enc_t encSource, cpi_enc_t encDest,bool); diff --git a/src/Notepad3.c b/src/Notepad3.c index c86ba722c..dd7a81580 100644 --- a/src/Notepad3.c +++ b/src/Notepad3.c @@ -4344,7 +4344,7 @@ LRESULT MsgCommand(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam) cpi_enc_t const iEncoding = Encoding_Current(CPI_GET); char chEncStrg[128] = { '\0' }; WideCharToMultiByte(Encoding_SciCP, 0, Encoding_GetLabel(iEncoding), -1, chEncStrg, COUNTOF(chEncStrg), NULL, NULL); - EditReplaceSelection(chEncStrg, true); + EditReplaceSelection(chEncStrg, false); } break; @@ -4428,7 +4428,7 @@ LRESULT MsgCommand(HWND hwnd, UINT umsg, WPARAM wParam, LPARAM lParam) StrTrimW(tchMaxPathBuffer, L"{}"); char chMaxPathBuffer[MAX_PATH] = { '\0' }; if (WideCharToMultiByte(Encoding_SciCP, 0, tchMaxPathBuffer, -1, chMaxPathBuffer, COUNTOF(chMaxPathBuffer), NULL, NULL)) { - EditReplaceSelection(chMaxPathBuffer, true); + EditReplaceSelection(chMaxPathBuffer, false); } } } @@ -6498,36 +6498,46 @@ bool HandleHotSpotURLClicked(const DocPos position, const HYPERLINK_OPS operatio if (SciCall_IndicatorValueAt(INDIC_NP3_HYPERLINK, position) == 0) { return false; } - char chURL[HUGE_BUFFER] = { '\0' }; - bool bHandled = false; DocPos const firstPos = SciCall_IndicatorStart(INDIC_NP3_HYPERLINK, position); DocPos const lastPos = SciCall_IndicatorEnd(INDIC_NP3_HYPERLINK, position); - DocPos const length = min_p(lastPos - firstPos, COUNTOF(chURL)); + DocPos const length = min_p(lastPos - firstPos, INTERNET_MAX_URL_LENGTH); - StringCchCopyNA(chURL, COUNTOF(chURL), SciCall_GetRangePointer(firstPos, length), length); - StrTrimA(chURL, " \t\n\r"); + const char* pszText = (const char*)SciCall_GetRangePointer(firstPos, length); - if (StrIsEmptyA(chURL)) { return bHandled; } + WCHAR szTextW[INTERNET_MAX_URL_LENGTH+1]; + int const cchTextW = MultiByteToWideChar(Encoding_SciCP, 0, pszText, (MBWC_DocPos_Cast)length, szTextW, COUNTOF(szTextW)); + szTextW[cchTextW] = L'\0'; - WCHAR wchURL[XHUGE_BUFFER] = { L'\0' }; - int const lenHypLnk = MultiByteToWideChar(Encoding_SciCP, 0, chURL, -1, wchURL, COUNTOF(wchURL)) - 1; + const WCHAR* chkPreFix = L"file://"; if (operation & COPY_HYPERLINK) { - if (lenHypLnk > 0) { - SetClipboardTextW(Globals.hwndMain, wchURL, lenHypLnk); + if (cchTextW > 0) { + + LPWSTR pszEscapedW = (LPWSTR)AllocMem(length * 3 * sizeof(WCHAR), HEAP_ZERO_MEMORY); + if (pszEscapedW == NULL) { + return false; + } + DWORD cchEscapedW = (DWORD)(length * 3); + DWORD const flags = (DWORD)(URL_BROWSER_MODE | URL_ESCAPE_AS_UTF8); + UrlEscape(szTextW, pszEscapedW, &cchEscapedW, flags); + + //SetClipboardTextW(Globals.hwndMain, szTextW, cchTextW); + SetClipboardTextW(Globals.hwndMain, pszEscapedW, cchEscapedW); + + FreeMem(pszEscapedW); bHandled = true; } } - else if ((operation & OPEN_WITH_NOTEPAD3) && (StrStrIA(chURL, "file://") == chURL)) + else if ((operation & OPEN_WITH_NOTEPAD3) && (StrStrI(szTextW, chkPreFix) == szTextW)) { - const WCHAR* chkPreFix = L"file://"; size_t const lenPfx = StringCchLenW(chkPreFix, 0); - WCHAR* szFileName = &(wchURL[lenPfx]); + WCHAR* szFileName = &(szTextW[lenPfx]); + szTextW[lenPfx + MAX_PATH] = L'\0'; // limit length StrTrimW(szFileName, L"/"); - PathCanonicalizeEx(szFileName, (DWORD)(COUNTOF(wchURL) - lenPfx)); + PathCanonicalizeEx(szFileName, (DWORD)(COUNTOF(szTextW) - lenPfx)); if (PathIsDirectory(szFileName)) { WCHAR tchFile[MAX_PATH] = { L'\0' }; @@ -6555,7 +6565,7 @@ bool HandleHotSpotURLClicked(const DocPos position, const HYPERLINK_OPS operatio sei.fMask = SEE_MASK_NOZONECHECKS; sei.hwnd = NULL; sei.lpVerb = NULL; - sei.lpFile = wchURL; + sei.lpFile = szTextW; sei.lpParameters = NULL; sei.lpDirectory = wchDirectory; sei.nShow = SW_SHOWNORMAL; diff --git a/src/TypeDefs.h b/src/TypeDefs.h index 0901bf7a8..77beafd60 100644 --- a/src/TypeDefs.h +++ b/src/TypeDefs.h @@ -601,6 +601,12 @@ typedef struct _docviewpos_t #define NOTEPAD3_MODULE_DIR_ENV_VAR L"NOTEPAD3MODULEDIR" +// from +#define INTERNET_MAX_PATH_LENGTH 2048 +#define INTERNET_MAX_SCHEME_LENGTH 32 // longest protocol name length +#define INTERNET_MAX_URL_LENGTH (INTERNET_MAX_SCHEME_LENGTH \ + + sizeof("://") \ + + INTERNET_MAX_PATH_LENGTH) //============================================================================= diff --git a/src/VersionEx.h b/src/VersionEx.h index a3ee7f682..636e686df 100644 --- a/src/VersionEx.h +++ b/src/VersionEx.h @@ -7,8 +7,8 @@ #define SAPPNAME "Notepad3" #define VERSION_MAJOR 5 #define VERSION_MINOR 19 -#define VERSION_REV 829 -#define VERSION_BUILD 2607 +#define VERSION_REV 830 +#define VERSION_BUILD 2609 #define SCINTILLA_VER 420 #define ONIGURUMA_REGEX_VER 6.9.3 #define UCHARDET_VER 2018.09.27 diff --git a/test/test_files/StyleLexers/styleLexTOML/TOML.toml b/test/test_files/StyleLexers/styleLexTOML/TOML.toml index 473cc1a02..2cb9f5733 100644 --- a/test/test_files/StyleLexers/styleLexTOML/TOML.toml +++ b/test/test_files/StyleLexers/styleLexTOML/TOML.toml @@ -2,6 +2,11 @@ # https://github.com/toml-lang/toml # +# https://fr.wikipedia.org/wiki/Caractères_de_civilité +# https://fr.wikipedia.org/wiki/Caract%C3%A8res_de_civilit%C3%A9 +# + + title = "TOML Example" [owner]