diff --git a/dat/HTMLTag-translations.ini b/dat/HTMLTag-translations.ini
index 28d2bb2..34596f3 100644
--- a/dat/HTMLTag-translations.ini
+++ b/dat/HTMLTag-translations.ini
@@ -11,6 +11,7 @@ menu_8 = "فك رموز شخصيات Unicode"
menu_9 = "تفعيل فك التشفير التلقائي الكيانات"
menu_10 = "تفعيل فك التشفير التلقائي أحرف Unicode"
menu_11 = "معلومة..."
+menu_12 = "إكمال HTML كيانات"
err_compat = "هذا الإصدار من HTML Tag يتطلب Notepad++ 8.3 أو أحدث. تم تعطيل أوامر البرنامج المساعد."
err_config = "ملف الكيان غير موجود"
err_config_msg = "في الملف|أو|يجب أن يكون في الملف"
@@ -28,6 +29,7 @@ menu_8 = "Desxifrar caràcters Unic&ode"
menu_9 = "Habilita desxifrat automàtica d'entitats"
menu_10 = "Habilita desxifrat automàtica de caràcters Unicode"
menu_11 = "Sob&re..."
+menu_12 = "Autocompleta entitats HTML"
err_compat = "Aquesta versió de HTML Tag requereix Notepad++ 8.3 o superior. Les funcions del plugin s'han desactivat."
err_config = "No s'ha trobat el fitxer de les entitats"
err_config_msg = "ha d'estar a la carpeta|o|a la carpeta"
@@ -45,6 +47,7 @@ menu_8 = "破译 Unicode 字符(&O)"
menu_9 = "启用自动实体解密"
menu_10 = "启用 Unicode 字符的自动解密"
menu_11 = "关于...(&A)"
+menu_12 = "实体自动补全"
err_compat = "这个版本的HTML Tag需要Notepad++ 8.3或更高版本。插件功能已被禁用。"
err_config = "未找到实体文件"
err_config_msg = "必须在文件中|或者|在文件中"
@@ -62,6 +65,7 @@ menu_8 = "Decodeer de Unic&ode-tekens"
menu_9 = "Activeer automatisch decoderen van entiteiten"
menu_10 = "Activeer automatisch decoderen van Unicode-tekens"
menu_11 = "&Info..."
+menu_12 = "HTML-entiteits aanvullen"
err_compat = "Deze versie van HTML Tag vereist Notepad++ 8.3 of een recentere versie. De plugin-opdrachten zijn uitgeschakeld."
err_config = "Entiteitenbestand niet gevonden"
err_config_msg = "moet in de map|of|in de map staan"
@@ -79,6 +83,7 @@ menu_8 = "ی کاراکترهای یونیکد ها را رمزگشایی کن
menu_9 = "رمزگشایی خودکار موجودیت را فعال کنید"
menu_10 = "رمزگشایی خودکار کاراکترهای یونیکد را فعال کنید"
menu_11 = "در باره..."
+menu_12 = "موارد HTML کامل"
err_compat = "این نسخه از HTML Tag به Notepad++ 8.3 یا جدیدتر نیاز دارد. دستورات افزونه غیرفعال هستند."
err_config = "فایل موجودیت یافت نشد"
err_config_msg = "در پرونده|یا|باید در پرونده باشد"
@@ -96,6 +101,7 @@ menu_8 = "Déchiffrer les caractères Unic&ode"
menu_9 = "Activer le déchiffrement automatique des entités"
menu_10 = "Activer le déchiffrement automatique des caractères Unicode"
menu_11 = "À &propos..."
+menu_12 = "Complétion d'entités HTML"
err_compat = "Cette version de HTML Tag nécessite Notepad++ 8.3 ou une version plus récente. Les commandes du plugin ont été désactivées."
err_config = "Fichier d'entités introuvable"
err_config_msg = "doit être dans le dossier|ou|dans le dossier"
@@ -113,6 +119,7 @@ menu_8 = "Unic&ode-Zeichen entschlüsseln"
menu_9 = "Automatische Entschlüsselung von Entitäten aktivieren"
menu_10 = "Automatische Entschlüsselung von Unicode-Zeichen aktivieren"
menu_11 = "Übe&r..."
+menu_12 = "HTML-Entitätsvervollständigung"
err_compat = "Diese Version von HTML Tag erfordert Notepad++ 8.3 oder höher. Die Befehle des Plugins wurden deaktiviert."
err_config = "Entitätsdatei nicht gefunden"
err_config_msg = "muss sich im Ordner|oder|im Ordner befinden"
@@ -130,6 +137,7 @@ menu_8 = "לפענח תווי Unicode"
menu_9 = "אפשר פענוח אוטומטי של ישויות"
menu_10 = "אפשר פענוח אוטומטי של תווי Unicode"
menu_11 = "על אודות..."
+menu_12 ="ישויות HTML מלאות"
err_compat = "גרסה זו של HTML Tag דורשת Notepad++ 8.3 ומעלה. פקודות פלאגין הושבתו."
err_config = "קובץ הישות לא נמצא"
err_config_msg = "בקובץ|אוֹ|זה חייב להיות בקובץ"
@@ -147,6 +155,7 @@ menu_8 = "यूनिकोड वर्णों को विकोड"
menu_9 = "स्वचालित इकाई डिक्रिप्शन सक्षम करें"
menu_10 = "यूनिकोड वर्णों का स्वचालित डिक्रिप्शन सक्षम करें"
menu_11 = "के संबंध में..."
+menu_12 = "HTML इकाई समापन"
err_compat = "HTML Tag के इस संस्करण के लिए Notepad++ 8.3 या नए संस्करण की आवश्यकता है। प्लगइन आदेश अक्षम कर दिए गए हैं."
err_config = "निकाय फ़ाइल नहीं मिली"
err_config_msg = "फ़ोल्डर में होना चाहिए|या|फ़ोल्डर में होना चाहिए"
@@ -164,6 +173,7 @@ menu_8 = "Decifra caratteri Unic&ode"
menu_9 = "Abilita decifrazione automatica delle entità"
menu_10 = "Abilita decifrazione automatica dei caratteri Unicode"
menu_11 = "&Informazioni..."
+menu_12 = "Completamento di entità HTML"
err_compat = "Questa versione di HTML Tag richiede Notepad++ 8.3 o versioni successive. I comandi del plugin sono stati disabilitati."
err_config = "File delle entità non trovato"
err_config_msg = "deve trovarsi nella cartella|o|nella cartella"
@@ -181,6 +191,7 @@ menu_8 = "Unicode 文字を解読する(&O)"
menu_9 = "エンティティの自動復号化を有効にする"
menu_10 = "Unicode 文字の自動復号化を有効にする"
menu_11 = "に関してで...(&A)"
+menu_12 = "HTML エンティティ補完"
err_compat = "このバージョンのHTML TagにはNotepad++8.3以降が必要です。プラグイン機能は無効になっています。"
err_config = "エンティティファイルが見つかりません"
err_config_msg = "ファイル内になければなりません|または|ファイル内に"
@@ -198,6 +209,7 @@ menu_8 = "유니코드 문자 해독(&O)"
menu_9 = "자동 엔티티 암호 해독 활성화"
menu_10 = "유니코드 문자의 자동 암호 해독 활성화"
menu_11 = "에 관해서...(&A)"
+menu_12 = "엔티티 완성"
err_compat = "이 버전의 HTML Tag에는 메모장++ 8.3 이상이 필요합니다. 플러그인 기능이 비활성화되었습니다."
err_config = "엔티티 파일을 찾을 수 없음"
err_config_msg = "파일에 있어야합니다|또는|파일에"
@@ -215,6 +227,7 @@ menu_8 = "Odszyfruj znaki U&nicode"
menu_9 = "Automatycznie odszyfruj jednostki"
menu_10 = "Automatycznie odszyfruj znaki Unicode"
menu_11 = "&O..."
+menu_12 = "Automatyczne uzupełnianie encji HTML"
err_compat = "Ta wersja HTML Tag wymaga Notepad++ 8.3 lub nowszego. Funkcje wtyczki zostały wyłączone."
err_config = "Nie znaleziono pliku encji"
err_config_msg = "musi znajdować się w folderu|lub|w folderu"
@@ -232,6 +245,7 @@ menu_8 = "Decifrar caracteres Unic&ode"
menu_9 = "Ativar decifração automatica das entidades"
menu_10 = "Ativar decifração automatica dos caracteres Unicode"
menu_11 = "Sob&re..."
+menu_12 = "Preencher entidades HTML"
err_compat = "Esta versão de HTML Tag requer o Notepad++ 8.3 ou superior. Os comandos de plugin foram desactivados."
err_config = "Arquivo de entidades não encontrado"
err_config_msg = "deve estar na pasta|ou|na pasta"
@@ -249,6 +263,7 @@ menu_8 = "Decifrar caracteres Unic&ode"
menu_9 = "Habilitar decifração automatica das entidades"
menu_10 = "Habilitar decifração automatica dos caracteres Unicode"
menu_11 = "Sob&re..."
+menu_12 = "Completar entidades HTML"
err_compat = "Esta versão de HTML Tag requer o Notepad++ 8.3 ou superior. Os comandos de plugin foram desactivados."
err_config = "Arquivo de entidades não encontrado"
err_config_msg = "deve estar na pasta|ou|na pasta"
@@ -266,6 +281,7 @@ menu_8 = "Decriptează caracterele Unic&ode"
menu_9 = "Activare decriptarea automată a entității"
menu_10 = "Activare decriptarea automată a caracterelor Unicode"
menu_11 = "Desp&re..."
+menu_12 = "Completare a HTML entități"
err_compat = "Această versiune de HTML Tag necesită Notepad++ 8.3 sau o versiune ulterioară. Comenzile pluginului au fost dezactivate."
err_config = "Fișierul entitățile nu a fost găsit"
err_config_msg = "trebuie să fie în folderul|sau|în folderul"
@@ -283,6 +299,7 @@ menu_8 = "Расшифровать символы Unicode"
menu_9 = "Включить автоматическую расшифровку сущностей"
menu_10 = "Включить автоматическую расшифровку символов Unicode"
menu_11 = "О..."
+menu_12 = "Завершение сущности HTML"
err_compat = "Эта версия HTML Tag требует Notepad++ 8.3 или выше. Функции плагина были отключены."
err_config = "Файл сущности не найден"
err_config_msg = "Это должно быть в файле|или|в файле"
@@ -300,6 +317,7 @@ menu_8 = "යුනිකෝඩ් අක්ෂර විකේතනය කර
menu_9 = "ස්වයංක්රීය ආයතන විකේතනය සබල කරන්න"
menu_10 = "යුනිකෝඩ් අක්ෂරවල ස්වයංක්රීය විකේතනය සක්රීය කරන්න"
menu_11 = "ගැන..."
+menu_12 = "HTML ආයතන සම්පූර්ණ කිරීම"
err_compat = "HTML Tag හි මෙම අනුවාදය සඳහා Notepad++ 8.3 හෝ නව අනුවාදයක් අවශ්ය වේ. ප්ලගින විධාන අක්රිය කර ඇත."
err_config = "ආයතන ගොනුව හමු නොවීය"
err_config_msg = "ෆෝල්ඩරයේ තිබිය යුතුය|හෝ|ෆෝල්ඩරයේ තිබිය"
@@ -317,6 +335,7 @@ menu_8 = "Descifrar caracteres Unic&ode"
menu_9 = "Habilitar desciframiento automático de entidades"
menu_10 = "Habilitar desciframiento automático de caracteres Unicode"
menu_11 = "&Acerca de..."
+menu_12 = "Autocompletar entidades HTML"
err_compat = "Esta versión de HTML Tag requiere Notepad++ 8.3 o superior. Los comandos del plugin se han desactivado."
err_config = "Archivo de entidades no encontrado"
err_config_msg = "debe estar en la carpeta|o|en la carpeta"
@@ -334,6 +353,7 @@ menu_8 = "Descifrar caracteres Unic&ode"
menu_9 = "Habilitar desciframiento automático de entidades"
menu_10 = "Habilitar desciframiento automático de caracteres Unicode"
menu_11 = "&Acerca de..."
+menu_12 = "Completar entidades HTML"
err_compat = "Esta versión de HTML Tag requiere Notepad++ 8.3 o superior. Los comandos del plugin se han desactivado."
err_config = "Archivo de entidades no encontrado"
err_config_msg = "debe estar en la carpeta|o|en la carpeta"
@@ -351,6 +371,7 @@ menu_8 = "யுனிகோட் எழுத்துகளை மறைக
menu_9 = "தன்னியக்க பொருள் மறைகுறியாக்கத்தை இயக்கு"
menu_10 = "யூனிகோட் எழுத்துகளின் தானியங்கி மறைகுறியாக்கத்தை இயக்கவும்"
menu_11 = "பற்றி..."
+menu_12 = "HTML உறுப்புகள் நிறைவு"
err_compat = "HTML Tag இன் இந்தப் பதிப்பிற்கு Notepad++ 8.3 அல்லது புதியது தேவைப்படுகிறது. செருகுநிரல் கட்டளைகள் முடக்கப்பட்டுள்ளன."
err_config = "என்டிட்டிகள் கோப்பு கிடைக்கவில்லை"
err_config_msg = "கோப்புறையில்|அல்லது|கோப்புறையில் இருக்க வேண்டும்"
@@ -368,6 +389,7 @@ menu_8 = "Розшифрувати символи Unicode"
menu_9 = "Включити автоматичне розшифрування сутностей"
menu_10 = "Увімкнути автоматичне розшифрування символів Unicode"
menu_11 = "Про..."
+menu_12 = "Завершення сутності HTML"
err_compat = "Ця версія HTML Tag потребує Notepad++ 8.3 або новішої версії. Функції плагіна було вимкнено."
err_config = "Файл сутності не знайдено"
err_config_msg = "Це має бути у файлі|або|у файлі"
diff --git a/src/HtmlTag.cpp b/src/HtmlTag.cpp
index 6e3a56c..992f09e 100644
--- a/src/HtmlTag.cpp
+++ b/src/HtmlTag.cpp
@@ -16,6 +16,7 @@
#include "Unicode.h"
#include "AboutDlg.h"
#include "HtmlTag.h"
+#include "XPM.h"
using namespace HtmlTag;
using namespace TextConv;
@@ -24,12 +25,15 @@ namespace fs = std::filesystem;
/////////////////////////////////////////////////////////////////////////////////////////
namespace {
enum DecodeCmd { dcAuto = -1, dcEntity, dcUnicode };
-enum CmdMenuPosition { cmpUnicode = 3, cmpEntities };
+enum CmdMenuPosition { cmpAcEntities = 3, cmpUnicode, cmpEntities };
bool menuLocaleIsRTL() noexcept;
+bool isWebDocument() noexcept;
bool autoCompleteMatchingTag(const Sci_Position startPos, const char *tagName);
+void autoCompleteEntity();
void findAndDecode(const int keyCode, DecodeCmd cmd = dcAuto);
+constexpr char acListKey[] = "_autocompletions";
constexpr char defaultUnicodePrefix[] = R"(\u)";
constexpr wchar_t menuItemSeparator[] = L"-";
constexpr wchar_t errorMessageDelimiter[] = L"|";
@@ -106,6 +110,10 @@ CMDMENUPROC toggleLiveUnicodeDecoding() {
plugin.toggleOption(&plugin.options.liveUnicodeDecoding, CmdMenuPosition::cmpUnicode);
}
// --------------------------------------------------------------------------------------
+CMDMENUPROC toggleEntityAutoCompletion() {
+ plugin.toggleOption(&plugin.options.entityAutoCompletion, CmdMenuPosition::cmpAcEntities);
+}
+// --------------------------------------------------------------------------------------
CMDMENUPROC commandAbout() {
if (!aboutHtmlTag)
aboutHtmlTag = std::make_unique(plugin.instance(), plugin.npp());
@@ -184,22 +192,44 @@ void HtmlTagPlugin::beNotified(SCNotification *scn) {
}
} else {
static bool isAutoCompletionCandidate = false;
+ static intptr_t acInsertMode = SC_MULTIAUTOC_ONCE;
switch (scn->nmhdr.code) {
case SCN_AUTOCSELECTION:
- if (isAutoCompletionCandidate && autoCompleteMatchingTag(scn->position, scn->text))
- plugin.editor().activeDocument().sendMessage(SCI_AUTOCCANCEL);
+ acInsertMode = editor().activeDocument().sendMessage(SCI_AUTOCGETMULTI);
+ if (isAutoCompletionCandidate && autoCompleteMatchingTag(scn->position, scn->text)) {
+ SciViewList views = editor().getViews();
+ for (size_t i = 0; i < views.size; ++i)
+ views[i].sendMessage(SCI_AUTOCSETMULTI, SC_MULTIAUTOC_EACH);
+ }
break;
+ case SCN_AUTOCCOMPLETED: {
+ SciViewList views = editor().getViews();
+ for (size_t i = 0; i < views.size; ++i)
+ views[i].sendMessage(SCI_AUTOCSETMULTI, acInsertMode);
+ break;
+ }
case SCN_AUTOCSELECTIONCHANGE: // https://www.scintilla.org/ScintillaDoc.html#SCN_AUTOCSELECTIONCHANGE
isAutoCompletionCandidate = (scn->listType == 0);
break;
case SCN_USERLISTSELECTION:
isAutoCompletionCandidate = false;
break;
+ case SCN_AUTOCCHARDELETED:
+ if (options.entityAutoCompletion && isWebDocument()) {
+ SciActiveDocument doc = editor().activeDocument();
+ if (doc.sendMessage(SCI_GETCHARAT, doc.currentPosition() - 1) == '&') {
+ autoCompleteEntity();
+ }
+ }
+ break;
case SCN_CHARADDED:
if ((scn->characterSource == SC_CHARACTERSOURCE_DIRECT_INPUT) &&
!plugin.editor().activeDocument().currentSelection()) {
findAndDecode(scn->ch);
}
+ if (options.entityAutoCompletion && isWebDocument() && (scn->ch == '&')) {
+ autoCompleteEntity();
+ }
break;
}
}
@@ -256,14 +286,17 @@ void HtmlTagPlugin::getEntities(EntityList &list) {
if (!config.GetAllKeys(listName, charRefs))
return;
+ std::stringstream acListBuf;
for (auto &&entity : charRefs) {
std::string codePointStr = config.GetValue(listName, entity.pItem);
int codePoint = std::stoi(codePointStr);
if (codePoint > 0) {
_entityMap[listName].addPair(entity.pItem, std::to_string(codePoint));
_entityMap[listName].addPair(std::to_string(codePoint), entity.pItem);
+ acListBuf << entity.pItem << ';' << '?' << XPM::getID() << ' ';
}
}
+ _entityMap[listName].addPair(acListKey, acListBuf.str());
list = _entityMap[listName];
} catch (...) {
config.~CSimpleIniTempl();
@@ -312,6 +345,7 @@ void HtmlTagPlugin::initMenu() {
funcItems.add(menuItemSeparator);
funcItems.add(getMessage(L"menu_9"), toggleLiveEntityecoding);
funcItems.add(getMessage(L"menu_10"), toggleLiveUnicodeDecoding);
+ funcItems.add(getMessage(L"menu_12"), toggleEntityAutoCompletion);
funcItems.add(menuItemSeparator);
funcItems.add(getMessage(L"menu_11"), commandAbout);
}
@@ -322,12 +356,20 @@ void HtmlTagPlugin::updateMenu() {
setLanguage();
loadTranslations();
+
+ constexpr int formerMaxIndex = 11; //< Index of the "About..." menu title before v1.5.2
HMENU hMenu = reinterpret_cast(sendNppMessage(NPPM_GETMENUHANDLE, NPPPLUGINMENU, nullptr));
for (intptr_t i = 0, menuId = 0; i < funcItems.count(); i++, menuId++) {
if (std::wcscmp(funcItems[i]._itemName, menuItemSeparator) == 0) {
menuId--;
continue;
+ } else if (menuId == formerMaxIndex) {
+ std::wstring titleOld = L"menu_" + std::to_wstring(formerMaxIndex);
+ std::wstring titleCurrent = L"menu_" + std::to_wstring(formerMaxIndex + 1);
+ std::wstring tmp(_menuTitles[titleCurrent]);
+ _menuTitles.addPair(titleCurrent, _menuTitles[titleOld]);
+ _menuTitles.addPair(titleOld, tmp);
}
MENUITEMINFOW mii;
@@ -391,6 +433,7 @@ void HtmlTagPlugin::loadOptions() {
return;
options.liveEntityDecoding = config.GetBoolValue("AUTO_DECODE", "ENTITIES", false);
options.liveUnicodeDecoding = config.GetBoolValue("AUTO_DECODE", "UNICODE_ESCAPE_CHARS", false);
+ options.entityAutoCompletion = config.GetBoolValue("AUTO_COMPLETE", "ENTITIES", true);
std::string userPrefix =
config.GetValue("FORMAT", "UNICODE_ESCAPE_PREFIX", defaultUnicodePrefix);
setUnicodeFormatOption(userPrefix);
@@ -402,8 +445,10 @@ void HtmlTagPlugin::loadOptions() {
setUnicodeFormatOption(defaultUnicodePrefix);
}
+ size_t autoCompleteEntities = funcItems.count() - CmdMenuPosition::cmpAcEntities;
size_t autoDecodeJs = funcItems.count() - CmdMenuPosition::cmpUnicode;
size_t autoDecodeEntities = funcItems.count() - CmdMenuPosition::cmpEntities;
+ funcItems[autoCompleteEntities]._init2Check = options.entityAutoCompletion;
funcItems[autoDecodeJs]._init2Check = options.liveUnicodeDecoding;
funcItems[autoDecodeEntities]._init2Check = options.liveEntityDecoding;
}
@@ -418,6 +463,7 @@ void HtmlTagPlugin::saveOptions() {
try {
config.SetLongValue("AUTO_DECODE", "ENTITIES", options.liveEntityDecoding);
config.SetLongValue("AUTO_DECODE", "UNICODE_ESCAPE_CHARS", options.liveUnicodeDecoding);
+ config.SetLongValue("AUTO_COMPLETE", "ENTITIES", options.entityAutoCompletion);
config.SetValue("FORMAT", "UNICODE_ESCAPE_PREFIX", options.unicodePrefix.c_str());
config.Save(ofs);
} catch (...) {
@@ -431,6 +477,7 @@ void HtmlTagPlugin::saveOptions() {
// --------------------------------------------------------------------------------------
MenuTitles::MenuTitles() : HashedStringList() {
std::initializer_list defaultMenuTitles = {
+ // clang-format off
L"menu_0=&Find matching tag",
L"menu_1=Select &matching tags",
L"menu_2=&Select tag and contents",
@@ -443,9 +490,10 @@ MenuTitles::MenuTitles() : HashedStringList() {
L"menu_9=Automatically decode entities",
L"menu_10=Automatically decode Unicode characters",
L"menu_11=&About...",
- L"err_compat=The installed version of HTML Tag requires Notepad++ 8.3 or newer. Plugin commands have "
- L"been disabled.",
+ L"menu_12=Auto-complete HTML entities",
+ L"err_compat=The installed version of HTML Tag requires Notepad++ 8.3 or newer. Plugin commands have been disabled.",
L"err_config=Missing Entities File",
+ // clang-format on
};
addStrings(defaultMenuTitles);
};
@@ -457,28 +505,40 @@ bool menuLocaleIsRTL() noexcept {
return std::find(rtlLangs.begin(), rtlLangs.end(), plugin.menuLocale()) != std::end(rtlLangs);
}
// --------------------------------------------------------------------------------------
-bool autoCompleteMatchingTag(const Sci_Position startPos, const char *tagName) {
- const size_t maxTagLength = 72; // https://www.rfc-editor.org/rfc/rfc1866#section-3.2.3
+bool isWebDocument() noexcept {
const auto webLangs = { L_HTML, L_XML, L_PHP, L_ASP, L_JSP };
- std::wstring newTag;
- bytesToText(tagName, newTag, CP_ACP);
+ return std::find(webLangs.begin(), webLangs.end(), plugin.documentLangType()) != std::end(webLangs);
+}
+// --------------------------------------------------------------------------------------
+void autoCompleteEntity() {
+ EntityList entities;
+ plugin.getEntities(entities);
+ SciActiveDocument doc = plugin.editor().activeDocument();
+ std::stringstream delimBuf;
+ delimBuf << static_cast(doc.sendMessage(SCI_AUTOCGETTYPESEPARATOR));
+ delimBuf << XPM::getID();
+ delimBuf << static_cast(doc.sendMessage(SCI_AUTOCGETSEPARATOR));
+ std::string acList = entities[{ acListKey }];
+ if (acList.find(delimBuf.str()) == std::string::npos) {
+ acList = std::regex_replace(acList, std::regex(R"(\?\d+ )"), delimBuf.str());
+ entities.addPair(acListKey, acList);
+ }
+ doc.sendMessage(SCI_REGISTERIMAGE, XPM::getID(), reinterpret_cast(XPM::getData()));
+ doc.sendMessage(SCI_AUTOCSHOW, UNUSEDW, &acList[0]);
+}
+// --------------------------------------------------------------------------------------
+bool autoCompleteMatchingTag(const Sci_Position startPos, const char *tagName) {
+ constexpr size_t maxTagLength = 72; // https://www.rfc-editor.org/rfc/rfc1866#section-3.2.3
+ SciActiveDocument doc = plugin.editor().activeDocument();
- if (std::find(webLangs.begin(), webLangs.end(), plugin.documentLangType()) != std::end(webLangs) ||
- plugin.editor().activeDocument().getSelectionMode() != smStreamMulti || newTag.length() > maxTagLength) {
+ if (!isWebDocument() || doc.getSelectionMode() != smStreamMulti || strlen(tagName) > maxTagLength) {
return false;
}
- SciActiveDocument doc = plugin.editor().activeDocument();
SciTextRange tagEnd{ doc };
doc.find(LR"([/>\s])", tagEnd, SCFIND_REGEXP, startPos, startPos + maxTagLength + 1);
- if (tagEnd.length() != 0) {
- commandSelectMatchingTags();
- doc.currentSelection() = newTag;
- return true;
- }
-
- return false;
+ return (tagEnd.length() != 0);
}
// --------------------------------------------------------------------------------------
void findAndDecode(const int keyCode, DecodeCmd cmd) {
@@ -522,8 +582,8 @@ void findAndDecode(const int keyCode, DecodeCmd cmd) {
}
if (chCurrent == plugin.options.unicodePrefix[0] &&
(plugin.options.liveUnicodeDecoding || cmd == dcUnicode)) { // Handle Unicode
- size_t lenPrefix = plugin.options.unicodePrefix.size();
- size_t lenCodePt = 4 + lenPrefix;
+ Sci_Position lenPrefix = static_cast(plugin.options.unicodePrefix.size());
+ Sci_Position lenCodePt = 4 + lenPrefix;
selStart = anchor;
// Backtrack to previous codepoint, in case it's part of a surrogate pair
chCurrent = static_cast(doc.sendMessage(SCI_GETCHARAT, anchor - lenCodePt));
@@ -536,7 +596,7 @@ void findAndDecode(const int keyCode, DecodeCmd cmd) {
}
}
didReplace = replace(Unicode::decode, selStart, caret);
- for (size_t i = 0; i < lenPrefix; i++) // Compensate for prefix length
+ for (Sci_Position i = 1; i < lenPrefix; ++i) // Compensate for prefix length
++charOffset;
break;
}
@@ -547,9 +607,11 @@ void findAndDecode(const int keyCode, DecodeCmd cmd) {
doc.currentPosition(doc.nextLineStartPosition());
} else {
nextCaretPos = doc.sendMessage(SCI_POSITIONAFTER, doc.currentPosition());
- if (nextCaretPos >= doc.nextLineStartPosition()) // Stay in current line if at EOL
+ if (nextCaretPos >= doc.nextLineStartPosition()) { // Stay in current line if at EOL
+ if (cmd == dcAuto) // ...but also stay ahead of the inserted char
+ doc.currentPosition(caret);
return;
-
+ }
if (cmd > dcAuto) // No inserted char, nothing to offset
charOffset = -1;
diff --git a/src/HtmlTag.h b/src/HtmlTag.h
index a25278a..e9480d7 100644
--- a/src/HtmlTag.h
+++ b/src/HtmlTag.h
@@ -20,6 +20,7 @@ DEFINE_ENUM_FLAG_OPERATORS(SelectionOptions)
struct PluginOptions {
BOOL liveEntityDecoding;
BOOL liveUnicodeDecoding;
+ BOOL entityAutoCompletion;
std::string unicodePrefix;
std::string unicodeRE;
};
diff --git a/src/LibNppPlugin/include/SciTextObjects.cpp b/src/LibNppPlugin/include/SciTextObjects.cpp
index 05dc32e..d7421a5 100644
--- a/src/LibNppPlugin/include/SciTextObjects.cpp
+++ b/src/LibNppPlugin/include/SciTextObjects.cpp
@@ -275,7 +275,7 @@ void SciTextRange::setText(std::wstring const &value) {
_editor.sendMessage(SCI_SETTARGETSTART, _startPos);
_editor.sendMessage(SCI_SETTARGETEND, _endPos);
nReplaced = _editor.sendMessage(sciMsg, txtRng, &chars[0]);
- _endPos -= (_endPos - _startPos) - nReplaced;
+ _startPos += nReplaced;
}
// --------------------------------------------------------------------------------------
Sci_Position SciTextRange::getStartCol() const {
diff --git a/src/LibNppPlugin/include/SciTextObjects.h b/src/LibNppPlugin/include/SciTextObjects.h
index 68c2bb3..4e12410 100644
--- a/src/LibNppPlugin/include/SciTextObjects.h
+++ b/src/LibNppPlugin/include/SciTextObjects.h
@@ -106,13 +106,13 @@ class SciTextRange {
virtual Sci_Position length() const { return getLength(); }
virtual const std::wstring text();
- virtual SciTextRange operator=(SciTextRange const &other) {
+ SciTextRange &operator=(SciTextRange const &other) {
setStart(other.getStart());
setEnd(other.getEnd());
return *this;
}
- virtual SciTextRange operator=(std::wstring const &value) {
+ virtual SciTextRange &operator=(std::wstring const &value) {
setText(value);
return *this;
}
@@ -162,7 +162,7 @@ class SciSelection final : public SciTextRange {
Sci_Position length() const override { return getLength(); }
const std::wstring text() override;
- SciTextRange operator=(std::wstring const &value) override {
+ SciSelection &operator=(std::wstring const &value) override {
setText(value);
return *this;
}
diff --git a/src/XPM.h b/src/XPM.h
new file mode 100644
index 0000000..5a56031
--- /dev/null
+++ b/src/XPM.h
@@ -0,0 +1,67 @@
+/*
+ This Source Code Form is subject to the terms of the Mozilla Public
+ License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ You can obtain one at https://mozilla.org/MPL/2.0/.
+
+ Copyright (c) 2024 Robert Di Pardo
+*/
+namespace HtmlTag {
+namespace Entities {
+ namespace XPM {
+ constexpr int decorationID = 0x7f;
+ constexpr int decorationDarkID = decorationID << 0x1;
+ constexpr const char *decoration[] = {
+ /* columns rows colors chars-per-pixel */
+ "16 16 2 1 ",
+ " c #262626",
+ "z c None",
+ /* pixels */
+ "zzzzzzzzzzzzzzzz",
+ "zzzzzz z zzzzzzz",
+ "zzzzz zzzzz",
+ "zzzz z zzzzz",
+ "zzzz zz zzzzz",
+ "zzzz z zzzzz",
+ "zzzzz zzzzzz",
+ "zzzzz zzzzzzz",
+ "zzz z zzz",
+ "zzz zz zzz",
+ "zz zzz zzz",
+ "zz zzzz zzz",
+ "zzz zz zz",
+ "zzzz zz zz",
+ "zzzzzzzzzzzz zzz",
+ "zzzzzzzzzzzzzzzz",
+ };
+ constexpr const char *decorationDark[] = {
+ /* columns rows colors chars-per-pixel */
+ "16 16 2 1 ",
+ " c #A0A0A0",
+ "z c None",
+ /* pixels */
+ "zzzzzzzzzzzzzzzz",
+ "zzzzzz z zzzzzzz",
+ "zzzzz zzzzz",
+ "zzzz z zzzzz",
+ "zzzz zz zzzzz",
+ "zzzz z zzzzz",
+ "zzzzz zzzzzz",
+ "zzzzz zzzzzzz",
+ "zzz z zzz",
+ "zzz zz zzz",
+ "zz zzz zzz",
+ "zz zzzz zzz",
+ "zzz zz zz",
+ "zzzz zz zz",
+ "zzzzzzzzzzzz zzz",
+ "zzzzzzzzzzzzzzzz",
+ };
+ inline int getID() {
+ return HtmlTag::plugin.isDarkModeEnabled() ? decorationDarkID : decorationID;
+ }
+ inline const char *const *getData() {
+ return HtmlTag::plugin.isDarkModeEnabled() ? decorationDark : decoration;
+ }
+ }
+}
+}