Fix clickable links break syntax highlighting issue

By using indicators instead of stylers to make code shorter and cleaner.

Fix #999, close #8263
This commit is contained in:
Udo Hoffmann 2020-05-13 19:00:44 +02:00 committed by Don HO
parent 9f29015a71
commit 4738c96318
No known key found for this signature in database
GPG Key ID: 6C429F1D8D84F46E
6 changed files with 55 additions and 156 deletions

View File

@ -2453,123 +2453,38 @@ void Notepad_plus::setUniModeText()
}
void Notepad_plus::addHotSpot()
void Notepad_plus::addHotSpot(ScintillaEditView* view)
{
ScintillaEditView* pView = view ? view : _pEditView;
int urlAction = (NppParameters::getInstance()).getNppGUI()._styleURL;
LPARAM Style = (urlAction == 2) ? INDIC_PLAIN : INDIC_HIDDEN;
pView->execute(SCI_INDICSETSTYLE, URL_INDIC, Style);
pView->execute(SCI_INDICSETHOVERSTYLE, URL_INDIC, INDIC_FULLBOX);
int startPos = 0;
int endPos = -1;
auto endStyle = _pEditView->execute(SCI_GETENDSTYLED);
pView->getVisibleStartAndEndPosition(&startPos, &endPos);
if (startPos >= endPos) return;
pView->execute(SCI_SETINDICATORCURRENT, URL_INDIC);
pView->execute(SCI_INDICATORCLEARRANGE, startPos, endPos - startPos);
if (!urlAction) return;
_pEditView->getVisibleStartAndEndPosition(&startPos, &endPos);
_pEditView->execute(SCI_SETSEARCHFLAGS, SCFIND_REGEXP|SCFIND_POSIX);
_pEditView->execute(SCI_SETTARGETRANGE, startPos, endPos);
std::vector<unsigned char> hotspotPairs; //= _pEditView->GetHotspotPairs();
unsigned char style_hotspot = 0;
unsigned char mask = 0x40; // INDIC1_MASK;
// INDIC2_MASK == 255 and it represents MSB bit
// only LEX_HTML and LEX_POSTSCRIPT use use INDIC2_MASK bit internally
// LEX_HTML is using INDIC2_MASK bit even though it has only 127 states, so it is safe to overwrite 8th bit
// INDIC2_MASK will be used for LEX_HTML
// LEX_POSTSCRIPT is using INDIC2_MASK bit for "tokenization", and is using mask=31 in lexer,
// therefore hotspot in LEX_POSTSCRIPT will be saved to 5th bit
// there are only 15 states in LEX_POSTSCRIPT, so it is safe to overwrite 5th bit
// rule of the thumb is, any lexet that calls: styler.StartAt(startPos, 255);
// must have special processing here, all other lexers are fine with INDIC1_MASK (7th bit)
LangType type = _pEditView->getCurrentBuffer()->getLangType();
if (type == L_HTML || type == L_PHP || type == L_ASP || type == L_JSP)
mask = 0x80; // INDIC2_MASK;
else if (type == L_PS)
mask = 16;
int posFound = static_cast<int32_t>(_pEditView->execute(SCI_SEARCHINTARGET, strlen(URL_REG_EXPR), reinterpret_cast<LPARAM>(URL_REG_EXPR)));
pView->execute(SCI_SETSEARCHFLAGS, SCFIND_REGEXP|SCFIND_POSIX);
pView->execute(SCI_SETTARGETRANGE, startPos, endPos);
int posFound = static_cast<int32_t>(pView->execute(SCI_SEARCHINTARGET, strlen(URL_REG_EXPR), reinterpret_cast<LPARAM>(URL_REG_EXPR)));
while (posFound != -1 && posFound != -2)
{
int start = int(_pEditView->execute(SCI_GETTARGETSTART));
int end = int(_pEditView->execute(SCI_GETTARGETEND));
int start = int(pView->execute(SCI_GETTARGETSTART));
int end = int(pView->execute(SCI_GETTARGETEND));
int foundTextLen = end - start;
unsigned char idStyle = static_cast<unsigned char>(_pEditView->execute(SCI_GETSTYLEAT, posFound));
// Search the style
int fs = -1;
for (size_t i = 0, len = hotspotPairs.size(); i < len ; ++i)
{
// make sure to ignore "hotspot bit" when comparing document style with archived hotspot style
if ((hotspotPairs[i] & ~mask) == (idStyle & ~mask))
{
fs = hotspotPairs[i];
_pEditView->execute(SCI_STYLEGETFORE, fs);
break;
}
}
// if we found it then use it to colourize
if (fs != -1)
{
_pEditView->execute(SCI_STARTSTYLING, start, 0xFF);
_pEditView->execute(SCI_SETSTYLING, foundTextLen, fs);
}
else // generalize a new style and add it into a array
{
style_hotspot = idStyle | mask; // set "hotspot bit"
hotspotPairs.push_back(style_hotspot);
unsigned char idStyleMSBunset = idStyle & ~mask;
char fontNameA[128];
Style hotspotStyle;
hotspotStyle._styleID = static_cast<int>(style_hotspot);
_pEditView->execute(SCI_STYLEGETFONT, idStyleMSBunset, reinterpret_cast<LPARAM>(fontNameA));
const size_t generic_fontnameLen = 128;
TCHAR *generic_fontname = new TCHAR[generic_fontnameLen];
WcharMbcsConvertor& wmc = WcharMbcsConvertor::getInstance();
const wchar_t * fontNameW = wmc.char2wchar(fontNameA, _nativeLangSpeaker.getLangEncoding());
wcscpy_s(generic_fontname, generic_fontnameLen, fontNameW);
hotspotStyle._fontName = generic_fontname;
hotspotStyle._fgColor = static_cast<COLORREF>(_pEditView->execute(SCI_STYLEGETFORE, idStyleMSBunset));
hotspotStyle._bgColor = static_cast<COLORREF>(_pEditView->execute(SCI_STYLEGETBACK, idStyleMSBunset));
hotspotStyle._fontSize = static_cast<int32_t>(_pEditView->execute(SCI_STYLEGETSIZE, idStyleMSBunset));
auto isBold = _pEditView->execute(SCI_STYLEGETBOLD, idStyleMSBunset);
auto isItalic = _pEditView->execute(SCI_STYLEGETITALIC, idStyleMSBunset);
auto isUnderline = _pEditView->execute(SCI_STYLEGETUNDERLINE, idStyleMSBunset);
hotspotStyle._fontStyle = (isBold?FONTSTYLE_BOLD:0) | (isItalic?FONTSTYLE_ITALIC:0) | (isUnderline?FONTSTYLE_UNDERLINE:0);
int urlAction = (NppParameters::getInstance()).getNppGUI()._styleURL;
if (urlAction == 2)
hotspotStyle._fontStyle |= FONTSTYLE_UNDERLINE;
_pEditView->setHotspotStyle(hotspotStyle);
_pEditView->execute(SCI_STYLESETHOTSPOT, style_hotspot, TRUE);
int activeFG = 0xFF0000;
Style *urlHovered = getStyleFromName(TEXT("URL hovered"));
if (urlHovered)
activeFG = urlHovered->_fgColor;
_pEditView->execute(SCI_SETHOTSPOTACTIVEFORE, TRUE, activeFG);
_pEditView->execute(SCI_SETHOTSPOTSINGLELINE, style_hotspot, 0);
// colourize it!
_pEditView->execute(SCI_STARTSTYLING, start, 0xFF);
_pEditView->execute(SCI_SETSTYLING, foundTextLen, style_hotspot);
}
_pEditView->execute(SCI_SETTARGETRANGE, posFound + foundTextLen, endPos);
posFound = static_cast<int32_t>(_pEditView->execute(SCI_SEARCHINTARGET, strlen(URL_REG_EXPR), reinterpret_cast<LPARAM>(URL_REG_EXPR)));
pView->execute(SCI_SETINDICATORCURRENT, URL_INDIC);
pView->execute(SCI_SETINDICATORVALUE, 0);
pView->execute(SCI_INDICATORFILLRANGE, start, foundTextLen);
pView->execute(SCI_SETTARGETRANGE, posFound + foundTextLen, endPos);
posFound = static_cast<int32_t>(pView->execute(SCI_SEARCHINTARGET, strlen(URL_REG_EXPR), reinterpret_cast<LPARAM>(URL_REG_EXPR)));
}
_pEditView->execute(SCI_STARTSTYLING, endStyle, 0xFF);
_pEditView->execute(SCI_SETSTYLING, 0, 0);
}
bool Notepad_plus::isConditionExprLine(int lineNumber)

View File

@ -62,6 +62,7 @@
#define TOOLBAR 0x02
#define URL_REG_EXPR "[A-Za-z]+://[A-Za-z0-9_\\-\\+~.:?&@=/%#,;\\{\\}\\(\\)\\[\\]\\|\\*\\!\\\\]+"
#define URL_INDIC 8
enum FileTransferMode {
TransferClone = 0x01,
@ -496,7 +497,7 @@ private:
int findMachedBracePos(size_t startPos, size_t endPos, char targetSymbol, char matchedSymbol);
void maintainIndentation(TCHAR ch);
void addHotSpot();
void addHotSpot(ScintillaEditView* view = NULL);
void bookmarkAdd(int lineno) const
{

View File

@ -2448,6 +2448,12 @@ LRESULT Notepad_plus::process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa
return TRUE;
}
case NPPM_INTERNAL_UPDATECLICKABLELINKS:
{
addHotSpot(_pEditView);
addHotSpot(_pNonEditView);
}
default:
{
if (message == WDN_NOTIFY)

View File

@ -611,9 +611,7 @@ BOOL Notepad_plus::notify(SCNotification *notification)
if (!_isFolding)
{
int urlAction = (NppParameters::getInstance()).getNppGUI()._styleURL;
if ((urlAction == 1) || (urlAction == 2))
addHotSpot();
addHotSpot();
}
if (_pDocMap)
@ -789,7 +787,22 @@ BOOL Notepad_plus::notify(SCNotification *notification)
}
}
}
else
{ // Double click with no modifiers
// Check wether cursor is within URL
auto indicMsk = notifyView->execute(SCI_INDICATORALLONFOR, notification->position);
if (!(indicMsk & (1 << URL_INDIC))) break;
// Revert selection of current word. Best to this early, otherwise the
// selected word is visible all the time while the browser is starting
notifyView->execute(SCI_SETSEL, notification->position, notification->position);
// Open URL
auto startPos = notifyView->execute(SCI_INDICATORSTART, URL_INDIC, notification->position);
auto endPos = notifyView->execute(SCI_INDICATOREND, URL_INDIC, notification->position);
generic_string url = notifyView->getGenericTextAsString(static_cast<size_t>(startPos), static_cast<size_t>(endPos));
::ShellExecute(_pPublicInterface->getHSelf(), TEXT("open"), url.c_str(), NULL, NULL, SW_SHOW);
}
break;
}
@ -804,9 +817,7 @@ BOOL Notepad_plus::notify(SCNotification *notification)
// replacement for obsolete custom SCN_SCROLLED
if (notification->updated & SC_UPDATE_V_SCROLL)
{
int urlAction = (NppParameters::getInstance()).getNppGUI()._styleURL;
if ((urlAction == 1) || (urlAction == 2))
addHotSpot();
addHotSpot();
}
// if it's searching/replacing, then do nothing
@ -973,9 +984,7 @@ BOOL Notepad_plus::notify(SCNotification *notification)
// if it's searching/replacing, then do nothing
if ((_linkTriggered && !nppParam._isFindReplacing) || notification->wParam == LINKTRIGGERED)
{
int urlAction = (NppParameters::getInstance()).getNppGUI()._styleURL;
if ((urlAction == 1) || (urlAction == 2))
addHotSpot();
addHotSpot();
_linkTriggered = false;
}
@ -987,42 +996,6 @@ BOOL Notepad_plus::notify(SCNotification *notification)
break;
}
case SCN_HOTSPOTDOUBLECLICK:
{
if (not notifyView)
return FALSE;
// Get the style and make sure it is a hotspot
uint8_t style = static_cast<uint8_t>(notifyView->execute(SCI_GETSTYLEAT, notification->position));
if (not notifyView->execute(SCI_STYLEGETHOTSPOT, style))
break;
long long startPos, endPos, docLen;
startPos = endPos = notification->position;
docLen = notifyView->getCurrentDocLen();
// Walk backwards/forwards to get the contiguous text in the same style
while (startPos > 0 && static_cast<uint8_t>(notifyView->execute(SCI_GETSTYLEAT, static_cast<WPARAM>(startPos - 1))) == style)
startPos--;
while (endPos < docLen && static_cast<uint8_t>(notifyView->execute(SCI_GETSTYLEAT, static_cast<WPARAM>(endPos))) == style)
endPos++;
// Select the entire link
notifyView->execute(SCI_SETANCHOR, static_cast<WPARAM>(startPos));
notifyView->execute(SCI_SETCURRENTPOS, static_cast<WPARAM>(endPos));
generic_string url = notifyView->getGenericTextAsString(static_cast<size_t>(startPos), static_cast<size_t>(endPos));
// remove the flickering: it seems a mouse left button up is missing after SCN_HOTSPOTDOUBLECLICK
::PostMessage(notifyView->getHSelf(), WM_LBUTTONUP, 0, 0);
auto curPos = notifyView->execute(SCI_GETCURRENTPOS);
notifyView->execute(SCI_SETSEL, curPos, curPos);
::ShellExecute(_pPublicInterface->getHSelf(), TEXT("open"), url.c_str(), NULL, NULL, SW_SHOW);
break;
}
case SCN_NEEDSHOWN:
{
break;

View File

@ -1004,6 +1004,8 @@ INT_PTR CALLBACK SettingsDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM)
::EnableWindow(::GetDlgItem(_hSelf, IDC_CHECK_CLICKABLELINK_NOUNDERLINE), isChecked);
nppGUI._styleURL = isChecked?2:0;
HWND grandParent = ::GetParent(_hParent);
::SendMessage(grandParent, NPPM_INTERNAL_UPDATECLICKABLELINKS, 0, 0);
}
return TRUE;
@ -1011,6 +1013,8 @@ INT_PTR CALLBACK SettingsDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM)
{
bool isChecked = isCheckedOrNot(IDC_CHECK_CLICKABLELINK_NOUNDERLINE);
nppGUI._styleURL = isChecked?1:2;
HWND grandParent = ::GetParent(_hParent);
::SendMessage(grandParent, NPPM_INTERNAL_UPDATECLICKABLELINKS, 0, 0);
}
return TRUE;

View File

@ -443,8 +443,8 @@
#define NPPM_INTERNAL_STOPMONITORING (NOTEPADPLUS_USER_INTERNAL + 49) // Used by Monitoring feature
#define NPPM_INTERNAL_EDGEBACKGROUND (NOTEPADPLUS_USER_INTERNAL + 50)
#define NPPM_INTERNAL_EDGEMULTISETSIZE (NOTEPADPLUS_USER_INTERNAL + 51)
//wParam: 0
//lParam: document new index
#define NPPM_INTERNAL_UPDATECLICKABLELINKS (NOTEPADPLUS_USER_INTERNAL + 52)
// See Notepad_plus_msgs.h
//#define NOTEPADPLUS_USER (WM_USER + 1000)