Skip to content

Commit

Permalink
Second attempt to make addHotSpot better -- no good
Browse files Browse the repository at this point in the history
  • Loading branch information
Coises committed Mar 22, 2024
1 parent 0bc28df commit 1061030
Showing 1 changed file with 172 additions and 44 deletions.
216 changes: 172 additions & 44 deletions PowerEditor/src/Notepad_plus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3431,56 +3431,184 @@ void Notepad_plus::addHotSpot(ScintillaEditView* view)
LRESULT indicFore = pView->execute(SCI_STYLEGETFORE, STYLE_DEFAULT);
pView->execute(SCI_SETINDICATORVALUE, indicFore);

UINT cp = static_cast<UINT>(pView->execute(SCI_GETCODEPAGE));
char* encodedText = nullptr;
try {
encodedText = new char[endPos - startPos + 1];
}
catch (const std::bad_alloc&)
{
return;
// Build vectors with leading and trailing parts of schemes to be recognized as the beginning of URL hot spots.
// All schemes must begin with a non-empty string of non-blank ASCII characters not including a colon; this string must be followed by a colon.
// Schemes can have a trailing string of non-blank ASCII characters which must follow the colon; this string can be empty.

std::vector<std::string> scheme1 {"ftp", "http", "https", "mailto", "file"};
std::vector<std::string> scheme2 {"//" , "//" , "//" , "" , "//" };
size_t longestScheme1 = 6;
size_t longestScheme2 = 2;
{
const auto& schemes = (NppParameters::getInstance()).getNppGUI()._uriSchemes;
int part = 0;
for (size_t i = 0; i < schemes.length(); ++i) {
if (schemes[i] > 126) continue;
const char c = static_cast<char>(schemes[i]);
switch (c) {
case ' ':
part = 0;
break;
case ':':
if (part == 1) part = 2;
else if (part == 2) {
scheme2.back() += ':';
if (scheme2.back().length() > longestScheme2) longestScheme2 = scheme2.back().length();
}
break;
default:
if (part == 1) { scheme1.back() += c; if (scheme1.back().length() > longestScheme1) longestScheme1 = scheme1.back().length(); }
else if (part == 2) { scheme2.back() += c; if (scheme2.back().length() > longestScheme2) longestScheme2 = scheme2.back().length(); }
else {
part = 1;
scheme1.emplace_back(1, c);
scheme2.emplace_back();
}
}
}
}

pView->getText(encodedText, startPos, endPos);
TCHAR* wideText = nullptr;
try
{
wideText = new TCHAR[endPos - startPos + 1];
}
catch (const std::bad_alloc&)
{
delete[] encodedText;
return;
// A Scintilla document is divided into two parts separated by a gap, which facilitates editing. This gap can be anywhere.
// In preparation for a fast scan to determine where URLs might be, find the Scintilla gap so we can access the document directly
// without forcing Scintilla to move the gap (which could adversly impact editing performance on large documents).
// If the gap isn't within the visible range, we have only section1; otherwise we must examine both section1 and section2.
// When we are done, the vector candidate will contain a list of positions and scheme lengths to be examined more carefully to

struct Candidate {
intptr_t position;
intptr_t length;
Candidate(intptr_t position, intptr_t length) : position(position), length(length) {}
};
std::vector<Candidate> candidate;

std::string_view section1;
std::string_view section2;
intptr_t gap = pView->execute(SCI_GETGAPPOSITION);
if (gap <= startPos || gap >= endPos) {
intptr_t length1 = endPos - startPos;
section1 = std::string_view(reinterpret_cast<const char*>(pView->execute(SCI_GETRANGEPOINTER, startPos, length1)), length1);
}
else {
intptr_t length1 = gap - startPos;
intptr_t length2 = endPos - gap;
section1 = std::string_view(reinterpret_cast<const char*>(pView->execute(SCI_GETRANGEPOINTER, startPos, length1)), length1);
section2 = std::string_view(reinterpret_cast<const char*>(pView->execute(SCI_GETRANGEPOINTER, gap , length2)), length2);
}

// First we look for colons, then see if the conditions for one of the schemes is met.
// Use lookNext and lookPrev to signal whether we need to be aware of the gap, either before or after the colon

string_view section = section1;
bool lookNext = !section2.empty();
bool lookPrev = false;
for (size_t colon = section.find_first_of(':');; colon = section.find_first_of(':', colon + 1)) {
if (colon == std::string::npos) {
if (!lookNext) break;
colon = section2.find_first_of(':');
if (colon == std::string::npos) break;
section = section2;
lookNext = false;
lookPrev = true;
}
for (size_t i = 0; i < scheme1.size(); ++i) {
size_t k1 = scheme1[i].length();
if (colon >= k1) {
if (colon > k1) { if (std::isalnum(static_cast<unsigned char>(section[colon - k1 - 1]))) continue; }
else if (lookPrev) { if (std::isalnum(static_cast<unsigned char>(section1.back()))) continue; }
if (strnicmp(section.substr(colon - k1).data(), scheme1[i].data(), k1) != 0) continue;
}
else {
if (!lookPrev) continue;
size_t q = k1 - colon;
if (q > section1.length()) continue;
if (q < section1.length()) { if (std::isalnum(static_cast<unsigned char>(section[section1.length() - q - 1]))) continue; }
if (strnicmp(section1.substr(section1.length() - q).data(), scheme1[i].data(), q) != 0) continue;
if (strnicmp(section2.data(), scheme1[i].substr(q).data(), colon) != 0) continue;
}
size_t k2 = scheme2[i].length();
if (colon + 1 + k2 <= section.length()) {
if (k2 != 0 && strnicmp(section.substr(colon + 1).data(), scheme2[i].data(), k2) != 0) continue;
}
else {
if (!lookNext) continue;
size_t p = section1.length() - colon - 1;
size_t q = k2 - p;
if (q > section2.length()) continue;
if (p != 0 && strnicmp(section.substr(colon + 1).data(), scheme2[i].data(), p) != 0) continue;
if (strnicmp(section2.data(), scheme2[i].substr(p).data(), q) != 0) continue;
}
candidate.emplace_back(colon + (lookPrev ? gap : startPos) - k1, k1 + k2 + 1);
break;
}
}

int wideTextLen = MultiByteToWideChar(cp, 0, encodedText, static_cast<int>(endPos - startPos + 1), (LPWSTR) wideText, static_cast<int>(endPos - startPos + 1)) - 1;
delete[] encodedText;
if (wideTextLen > 0)
{
int startWide = 0;
int lenWide = 0;
int startEncoded = 0;
int lenEncoded = 0;
while (true)
{
bool r = isUrl(wideText, wideTextLen, startWide, & lenWide);
if (lenWide <= 0)
break;
assert ((startWide + lenWide) <= wideTextLen);
lenEncoded = WideCharToMultiByte(cp, 0, & wideText [startWide], lenWide, NULL, 0, NULL, NULL);
if (r)
pView->execute(SCI_INDICATORFILLRANGE, startEncoded + startPos, lenEncoded);
else
pView->execute(SCI_INDICATORCLEARRANGE, startEncoded + startPos, lenEncoded);
startWide += lenWide;
startEncoded += lenEncoded;
if ((startWide >= wideTextLen) || ((startEncoded + startPos) >= endPos))
break;
const char rxURL[] = "\\G([0-9A-Z:/#@!$&*+,;=\\-._~%[\\]']+|\\((?1)*+\\))+"
"(?:\\?([0-9A-Z:/?#@!$&*+,;=\\-._~%]+|\\((?2)*+\\)|\\[(?2)*+\\]|\\{(?2)*+\\}|\"[^\"\\r\\n]*+\"|'[^'\\r\\n]*+')+)?"
"(?<![,;#\\!\\?])";
pView->execute(SCI_SETSEARCHFLAGS, SCFIND_REGEXP | SCFIND_POSIX);
intptr_t lastUpdated = startPos;
for (const auto& url : candidate) {
pView->execute(SCI_SETTARGETRANGE, url.position + url.length, endPos);
intptr_t body = pView->execute(SCI_SEARCHINTARGET, sizeof(rxURL) - 1, reinterpret_cast<LPARAM>(rxURL));
if (body > 0) {
if (url.position > lastUpdated) pView->execute(SCI_INDICATORCLEARRANGE, lastUpdated, url.position - lastUpdated);
lastUpdated = pView->execute(SCI_GETTARGETEND);
pView->execute(SCI_INDICATORFILLRANGE, url.position, lastUpdated - url.position);
}
assert ((startEncoded + startPos) == endPos);
assert (startWide == wideTextLen);
}
delete[] wideText;
if (lastUpdated < endPos) pView->execute(SCI_INDICATORCLEARRANGE, lastUpdated, endPos - lastUpdated);


// UINT cp = static_cast<UINT>(pView->execute(SCI_GETCODEPAGE));
// char* encodedText = nullptr;
// try {
// encodedText = new char[endPos - startPos + 1];
// }
// catch (const std::bad_alloc&)
// {
// return;
// }
//
// pView->getText(encodedText, startPos, endPos);
// TCHAR* wideText = nullptr;
// try
// {
// wideText = new TCHAR[endPos - startPos + 1];
// }
// catch (const std::bad_alloc&)
// {
// delete[] encodedText;
// return;
// }
//
// int wideTextLen = MultiByteToWideChar(cp, 0, encodedText, static_cast<int>(endPos - startPos + 1), (LPWSTR) wideText, static_cast<int>(endPos - startPos + 1)) - 1;
// delete[] encodedText;
// if (wideTextLen > 0)
// {
// int startWide = 0;
// int lenWide = 0;
// int startEncoded = 0;
// int lenEncoded = 0;
// while (true)
// {
// bool r = isUrl(wideText, wideTextLen, startWide, & lenWide);
// if (lenWide <= 0)
// break;
// assert ((startWide + lenWide) <= wideTextLen);
// lenEncoded = WideCharToMultiByte(cp, 0, & wideText [startWide], lenWide, NULL, 0, NULL, NULL);
// if (r)
// pView->execute(SCI_INDICATORFILLRANGE, startEncoded + startPos, lenEncoded);
// else
// pView->execute(SCI_INDICATORCLEARRANGE, startEncoded + startPos, lenEncoded);
// startWide += lenWide;
// startEncoded += lenEncoded;
// if ((startWide >= wideTextLen) || ((startEncoded + startPos) >= endPos))
// break;
// }
// assert ((startEncoded + startPos) == endPos);
// assert (startWide == wideTextLen);
// }
// delete[] wideText;
}

bool Notepad_plus::isConditionExprLine(intptr_t lineNumber)
Expand Down

0 comments on commit 1061030

Please sign in to comment.