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; + } + } +} +}