From c2e627bcaa5caaf6218c7840640395af45993969 Mon Sep 17 00:00:00 2001 From: Rainer Kottenhoff Date: Tue, 12 May 2026 14:59:54 +0200 Subject: [PATCH 1/2] fix: small optimization of PCRE2 regEx backward search --- .../pcre2/scintilla/PCRE2RegExEngine.cxx | 62 ++++++++++++------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/scintilla/pcre2/scintilla/PCRE2RegExEngine.cxx b/scintilla/pcre2/scintilla/PCRE2RegExEngine.cxx index 9fb09af17..3b7aae97b 100644 --- a/scintilla/pcre2/scintilla/PCRE2RegExEngine.cxx +++ b/scintilla/pcre2/scintilla/PCRE2RegExEngine.cxx @@ -73,7 +73,7 @@ class PCRE2RegExEngine : public RegexSearchBase public: explicit PCRE2RegExEngine(CharClassify* /*charClassTable*/) - : m_CompileOptions(PCRE2_UTF | PCRE2_UCP | PCRE2_MULTILINE) + : m_CompileOptions(PCRE2_UTF | PCRE2_UCP | PCRE2_MULTILINE | PCRE2_USE_OFFSET_LIMIT) , m_CompiledPattern(nullptr) , m_MatchData(nullptr) , m_CompileContext(nullptr) @@ -261,8 +261,11 @@ Sci::Position PCRE2RegExEngine::FindText(Document* doc, Sci::Position minPos, Sc // --- Build compile options --- // PCRE2_MULTILINE: ^/$ match at line boundaries + // PCRE2_USE_OFFSET_LIMIT: enables pcre2_set_offset_limit() on the match context; + // set unconditionally so the bit is part of the recompile cache key and + // forward/backward share one compiled pattern. // Newline convention (ANYCRLF) is set on m_CompileContext, not here - uint32_t compileOptions = PCRE2_UTF | PCRE2_UCP | PCRE2_MULTILINE; + uint32_t compileOptions = PCRE2_UTF | PCRE2_UCP | PCRE2_MULTILINE | PCRE2_USE_OFFSET_LIMIT; if (!caseSensitive) { compileOptions |= PCRE2_CASELESS; @@ -346,6 +349,9 @@ Sci::Position PCRE2RegExEngine::FindText(Document* doc, Sci::Position minPos, Sc if (findForward) { // --- Forward search --- + // Clear any offset_limit a prior backward call left on the shared match context. + pcre2_set_offset_limit(m_MatchContext, PCRE2_UNSET); + int rc = pcre2_match( m_CompiledPattern, docBegPtr, @@ -378,11 +384,21 @@ Sci::Position PCRE2RegExEngine::FindText(Document* doc, Sci::Position minPos, Sc // range forward (O(n)), we search in reverse chunks starting near rangeEnd. // Each chunk searches forward within a window and keeps the last match. // Average case is O(chunk_size) instead of O(range_size). + // + // PCRE2_USE_OFFSET_LIMIT (compile flag) + pcre2_set_offset_limit() let the + // engine itself prune match attempts whose start position would exceed + // rangeEnd, so the inner loop needs no post-match bounds check. + pcre2_set_offset_limit(m_MatchContext, static_cast(rangeEnd)); Sci::Position lastMatchPos = SciPos(-1); Sci::Position lastMatchLen = SciPos(0); - pcre2_match_data* iterMatchData = pcre2_match_data_create_from_pattern(m_CompiledPattern, nullptr); + // Snapshot the ovector of the rightmost successful match so the loop can + // keep advancing without losing it. Sized for the same cap that + // SubstituteByPosition supports ($0..$99 → 100 pairs). + constexpr size_t kMaxOvecPairs = 100; + PCRE2_SIZE savedOvec[kMaxOvecPairs * 2]; + uint32_t savedOvecCount = 0; Sci::Position const chunkSize = 4096; // search window size Sci::Position chunkStart = (rangeEnd > chunkSize) ? (rangeEnd - chunkSize) : rangeBeg; @@ -403,25 +419,28 @@ Sci::Position PCRE2RegExEngine::FindText(Document* doc, Sci::Position minPos, Sc docLen, searchStart, matchOptions, - iterMatchData, + m_MatchData, m_MatchContext ); - if (rc <= 0) break; // no more matches + if (rc <= 0) break; // no more matches (offset_limit enforces upper bound) - PCRE2_SIZE* ovector = pcre2_get_ovector_pointer(iterMatchData); - Sci::Position pos = SciPos(ovector[0]); - Sci::Position len = SciPos(ovector[1]) - pos; + PCRE2_SIZE* ovector = pcre2_get_ovector_pointer(m_MatchData); + uint32_t ovecCount = pcre2_get_ovector_count(m_MatchData); + if (ovecCount > kMaxOvecPairs) ovecCount = kMaxOvecPairs; + memcpy(savedOvec, ovector, ovecCount * 2 * sizeof(PCRE2_SIZE)); + savedOvecCount = ovecCount; - if (pos > rangeEnd) break; // past search range + Sci::Position pos = SciPos(savedOvec[0]); + Sci::Position len = SciPos(savedOvec[1]) - pos; lastMatchPos = pos; lastMatchLen = len; found = true; // Advance past this match (at least 1 byte, handle UTF-8) - if (ovector[1] > ovector[0]) { - searchStart = ovector[1]; + if (savedOvec[1] > savedOvec[0]) { + searchStart = savedOvec[1]; } else { searchStart = static_cast( doc->MovePositionOutsideChar(SciPos(searchStart + 1), 1, true) @@ -442,21 +461,16 @@ Sci::Position PCRE2RegExEngine::FindText(Document* doc, Sci::Position minPos, Sc } } - // Re-run the match at the final position to populate m_MatchData for SubstituteByPosition - if (lastMatchPos >= 0) { - pcre2_match( - m_CompiledPattern, - docBegPtr, - docLen, - static_cast(lastMatchPos), - matchOptions | PCRE2_ANCHORED, - m_MatchData, - m_MatchContext - ); + // Restore the rightmost-match ovector into m_MatchData so SubstituteByPosition + // sees the correct groups. The loop's final pcre2_match call returned + // PCRE2_ERROR_NOMATCH and left m_MatchData's ovector in an undefined state; + // memcpy'ing the snapshot back is cheaper than a re-anchored pcre2_match call. + if (lastMatchPos >= 0 && savedOvecCount > 0) { + memcpy(pcre2_get_ovector_pointer(m_MatchData), + savedOvec, + savedOvecCount * 2 * sizeof(PCRE2_SIZE)); } - pcre2_match_data_free(iterMatchData); - m_MatchPos = lastMatchPos; m_MatchLen = lastMatchLen; } From da478e46994cd80e7d15fae58bdff5d1f3e3494a Mon Sep 17 00:00:00 2001 From: Rainer Kottenhoff Date: Tue, 12 May 2026 15:46:05 +0200 Subject: [PATCH 2/2] chr: option to show non-print characters --- Readme.md | 2 +- language/common_res.h | 1 + language/np3_af_za/menu_af_za.rc | 1 + language/np3_be_by/menu_be_by.rc | 1 + language/np3_de_de/menu_de_de.rc | 1 + language/np3_el_gr/menu_el_gr.rc | 1 + language/np3_en_gb/menu_en_gb.rc | 1 + language/np3_en_us/menu_en_us.rc | 1 + language/np3_es_es/menu_es_es.rc | 1 + language/np3_fi_fi/menu_fi_fi.rc | 1 + language/np3_fr_fr/menu_fr_fr.rc | 1 + language/np3_hi_in/menu_hi_in.rc | 1 + language/np3_hu_hu/menu_hu_hu.rc | 1 + language/np3_id_id/menu_id_id.rc | 1 + language/np3_it_it/menu_it_it.rc | 1 + language/np3_ja_jp/menu_ja_jp.rc | 1 + language/np3_ko_kr/menu_ko_kr.rc | 1 + language/np3_nl_nl/menu_nl_nl.rc | 1 + language/np3_pl_pl/menu_pl_pl.rc | 1 + language/np3_pt_br/menu_pt_br.rc | 1 + language/np3_pt_pt/menu_pt_pt.rc | 1 + language/np3_ru_ru/menu_ru_ru.rc | 1 + language/np3_sk_sk/menu_sk_sk.rc | 1 + language/np3_sv_se/menu_sv_se.rc | 1 + language/np3_tr_tr/menu_tr_tr.rc | 1 + language/np3_vi_vn/menu_vi_vn.rc | 1 + language/np3_zh_cn/menu_zh_cn.rc | 1 + language/np3_zh_tw/menu_zh_tw.rc | 1 + src/Config/Config.cpp | 2 + src/Notepad3.c | 6 ++ src/Styles.c | 84 +++++++++++++++++++++++++ src/Styles.h | 1 + src/TypeDefs.h | 1 + test/test_files/non_printable_chars.txt | 69 ++++++++++++++++++++ todo/TODO.md | 29 ++++----- 35 files changed, 206 insertions(+), 15 deletions(-) create mode 100644 test/test_files/non_printable_chars.txt diff --git a/Readme.md b/Readme.md index ce1be2c35..29206f375 100644 --- a/Readme.md +++ b/Readme.md @@ -29,7 +29,7 @@ Notepad3 is a free, open-source text editor with syntax highlighting for Windows - **Mark all occurrences** of a selected word with occurrence count display - **Find and Replace** with [PCRE2](https://github.com/PCRE2Project/pcre2) regular expression engine - **[Focused View](readme/focusedview/FocusedView.md)** — filter display to show only lines matching the current word/selection (Ctrl+Alt+V), with fold, bookmark, and highlight modes -- **[TinyExpr++](https://blake-madden.github.io/tinyexpr-plusplus/)** — inline math expression evaluation with 80+ functions (type `expression=?` to evaluate in-place, use `${expr}` in line numbering). See the [Expression Engine Guide](readme/tinyexprcpp/TinyExprPP.md) for details. +- **[TinyExpr++](readme/tinyexprcpp/TinyExprPP.md)** — inline math expression evaluation with 80+ functions (type `expression=?` to evaluate in-place, use `${expr}` in line numbering). - **Undo/Redo** that preserves selection state - **Visual Studio–style** copy/paste of the current line (when nothing is selected) - **Virtual space** rectangular selection (Alt+Drag) diff --git a/language/common_res.h b/language/common_res.h index f96a547af..6083e232c 100644 --- a/language/common_res.h +++ b/language/common_res.h @@ -814,6 +814,7 @@ #define IDM_VIEW_CHGHIST_ALL 41062 // SC_CHANGE_HISTORY_ENABLED | SC_CHANGE_HISTORY_MARKERS | SC_CHANGE_HISTORY_INDICATORS #define IDM_VIEW_CHGHIST_TOGGLE_MARGIN 41063 #define IDM_VIEW_CHGHIST_CLEAR_UNDOREDO 41064 +#define IDM_VIEW_NONPRINTINGCHARS 41065 // keep Scintilla(SC) order #define IDM_SET_RENDER_TECH_GDI 42000 // SC_TECHNOLOGY_DEFAULT(0) diff --git a/language/np3_af_za/menu_af_za.rc b/language/np3_af_za/menu_af_za.rc index e97168240..ecb66c4f3 100644 --- a/language/np3_af_za/menu_af_za.rc +++ b/language/np3_af_za/menu_af_za.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "Wys &Oorvloei Simbole\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "W&ys Oop Spasies\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "Wys Lyn &Einde\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "Verander Geskiedenismerker" BEGIN diff --git a/language/np3_be_by/menu_be_by.rc b/language/np3_be_by/menu_be_by.rc index 36703c768..2b642a54d 100644 --- a/language/np3_be_by/menu_be_by.rc +++ b/language/np3_be_by/menu_be_by.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "Паказваць &сімвалы пераносаў\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "Паказваць прабельныя сімва&лы\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "Паказваць &канчаткі радкоў\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "Маркер гісторыі змяненняў" BEGIN diff --git a/language/np3_de_de/menu_de_de.rc b/language/np3_de_de/menu_de_de.rc index 81b039e55..b5ef3edf6 100644 --- a/language/np3_de_de/menu_de_de.rc +++ b/language/np3_de_de/menu_de_de.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "Zeige Umbruchsymbole\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "Zeige Leerzeichen\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "Zeige Zeilenumbrüche\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Zeige nicht-druckbare &Zeichen", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "Änderungshistorie Markierung" BEGIN diff --git a/language/np3_el_gr/menu_el_gr.rc b/language/np3_el_gr/menu_el_gr.rc index 704a71673..f11abc6ef 100644 --- a/language/np3_el_gr/menu_el_gr.rc +++ b/language/np3_el_gr/menu_el_gr.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "Εμφάνιση &συμβόλων αναδίπλωσης\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "Εμφάνιση &κενών διαστημάτων\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "Εμφάνιση καταλή&ξεων γραμμών\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "Ενδείξεις αλλαγών" BEGIN diff --git a/language/np3_en_gb/menu_en_gb.rc b/language/np3_en_gb/menu_en_gb.rc index e22af03e5..a23a8c91c 100644 --- a/language/np3_en_gb/menu_en_gb.rc +++ b/language/np3_en_gb/menu_en_gb.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "Show W&rap Symbols\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "Show &Blanks\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "Show Line &Endings\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "Change History Marker" BEGIN diff --git a/language/np3_en_us/menu_en_us.rc b/language/np3_en_us/menu_en_us.rc index 32414051a..4e34747ea 100644 --- a/language/np3_en_us/menu_en_us.rc +++ b/language/np3_en_us/menu_en_us.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "Show W&rap Symbols\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "Show &Blanks\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "Show Line &Endings\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "Change History Marker" BEGIN diff --git a/language/np3_es_es/menu_es_es.rc b/language/np3_es_es/menu_es_es.rc index 7ad2df4d2..28b213973 100644 --- a/language/np3_es_es/menu_es_es.rc +++ b/language/np3_es_es/menu_es_es.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "Mostrar &símbolos de ajuste\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "Mostrar los &Blancos\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "Mostrar &fin de línea\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "Marcadores historial de cambios" BEGIN diff --git a/language/np3_fi_fi/menu_fi_fi.rc b/language/np3_fi_fi/menu_fi_fi.rc index 1efcebdde..c4d301bb1 100644 --- a/language/np3_fi_fi/menu_fi_fi.rc +++ b/language/np3_fi_fi/menu_fi_fi.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "Näytä &rivityssymbolit\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "Näytä &tyhjät tilat\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "Näytä rivin lop&etukset\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "Vaihda historiamerkintä" BEGIN diff --git a/language/np3_fr_fr/menu_fr_fr.rc b/language/np3_fr_fr/menu_fr_fr.rc index 31aeb3921..12f44bf71 100644 --- a/language/np3_fr_fr/menu_fr_fr.rc +++ b/language/np3_fr_fr/menu_fr_fr.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "Afficher les symbôles de sauts de ligne\tCtrl+Maj+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "Afficher les &blancs\tCtrl+Maj+8", IDM_VIEW_SHOWBLANKS MENUITEM "Afficher les fins de lign&e\tCtrl+Maj+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "Marqueurs d'historique des modifications" BEGIN diff --git a/language/np3_hi_in/menu_hi_in.rc b/language/np3_hi_in/menu_hi_in.rc index a9d4bf29b..b6ae0e093 100644 --- a/language/np3_hi_in/menu_hi_in.rc +++ b/language/np3_hi_in/menu_hi_in.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "लपेटे हुए चिन्ह दिखाएं (&P)\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "खाली स्थान दिखाएं (&B)\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "वाक्य अंत दिखाएं (&E)\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "परिवर्तन इतिहास मार्कर" BEGIN diff --git a/language/np3_hu_hu/menu_hu_hu.rc b/language/np3_hu_hu/menu_hu_hu.rc index 24d5f8854..46df446ba 100644 --- a/language/np3_hu_hu/menu_hu_hu.rc +++ b/language/np3_hu_hu/menu_hu_hu.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "Sortö&rés jelzése\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "Sz&óközök megjelenítése\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "Sorvégek &megjelenítése\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "Előzményjelölők megváltoztatása" BEGIN diff --git a/language/np3_id_id/menu_id_id.rc b/language/np3_id_id/menu_id_id.rc index 13e041fcc..ea1331597 100644 --- a/language/np3_id_id/menu_id_id.rc +++ b/language/np3_id_id/menu_id_id.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "Tampilkan Simbol Bungkus Kata\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "Tampilkan Spasi\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "Tampilkan Akhir Baris\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "Penanda Riwayat" BEGIN diff --git a/language/np3_it_it/menu_it_it.rc b/language/np3_it_it/menu_it_it.rc index 7e7ff82bd..bfadf2127 100644 --- a/language/np3_it_it/menu_it_it.rc +++ b/language/np3_it_it/menu_it_it.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "Visualizza simboli di a capo\tCtrl+Maiusc+0", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "Visualizza spazi\tCtrl+Maiusc+8", IDM_VIEW_SHOWBLANKS MENUITEM "Visualizza terminazioni di linea\tCtrl+Maiusc+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "Marcatore cronologia modifiche" BEGIN diff --git a/language/np3_ja_jp/menu_ja_jp.rc b/language/np3_ja_jp/menu_ja_jp.rc index 681f04f0e..fed01a54f 100644 --- a/language/np3_ja_jp/menu_ja_jp.rc +++ b/language/np3_ja_jp/menu_ja_jp.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "折り返し記号(&P)\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "空白(&W)\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "改行コード(&E)\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "変更履歴の目印" BEGIN diff --git a/language/np3_ko_kr/menu_ko_kr.rc b/language/np3_ko_kr/menu_ko_kr.rc index 761853694..94b559045 100644 --- a/language/np3_ko_kr/menu_ko_kr.rc +++ b/language/np3_ko_kr/menu_ko_kr.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "줄 바꿈 기호 표시(&P)\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "공백 표시(&B)\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "줄 끝 표시(&E)\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "사용 내역 마커 변경" BEGIN diff --git a/language/np3_nl_nl/menu_nl_nl.rc b/language/np3_nl_nl/menu_nl_nl.rc index 43bead908..8bf4f02a0 100644 --- a/language/np3_nl_nl/menu_nl_nl.rc +++ b/language/np3_nl_nl/menu_nl_nl.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "Terugloo&pmarkeringen weergeven\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "&Blanco's weergeven\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "Regel&einden weergeven\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "Markering van wijzigingen" BEGIN diff --git a/language/np3_pl_pl/menu_pl_pl.rc b/language/np3_pl_pl/menu_pl_pl.rc index 246419dd1..2f528e304 100644 --- a/language/np3_pl_pl/menu_pl_pl.rc +++ b/language/np3_pl_pl/menu_pl_pl.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "Pokaż symbole zawijania wiersza\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "Pokaż niewidoczne znaki \tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "Pokaż znaki końca wiersza\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "Zmień marker historii" BEGIN diff --git a/language/np3_pt_br/menu_pt_br.rc b/language/np3_pt_br/menu_pt_br.rc index dd987cf26..4d2eb4039 100644 --- a/language/np3_pt_br/menu_pt_br.rc +++ b/language/np3_pt_br/menu_pt_br.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "Mostrar Símbolos de Q&uebra\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "Mostrar &Espaços\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "Mostrar &Finais de Linha\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "Alterar Marcador de Histórico" BEGIN diff --git a/language/np3_pt_pt/menu_pt_pt.rc b/language/np3_pt_pt/menu_pt_pt.rc index cfb9984d2..5ebfd16de 100644 --- a/language/np3_pt_pt/menu_pt_pt.rc +++ b/language/np3_pt_pt/menu_pt_pt.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "Mostrar símbolos de mudança de linha\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "Mostrar espaços\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "Mostrar fins d&e linha\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "Marcador do histórico de alterações" BEGIN diff --git a/language/np3_ru_ru/menu_ru_ru.rc b/language/np3_ru_ru/menu_ru_ru.rc index c815dfcd1..bc32dfc74 100644 --- a/language/np3_ru_ru/menu_ru_ru.rc +++ b/language/np3_ru_ru/menu_ru_ru.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "Отображать &символы переноса\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "Отображать про&бельные символы\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "Отображать &окончания строк\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "Маркер истории изменений" BEGIN diff --git a/language/np3_sk_sk/menu_sk_sk.rc b/language/np3_sk_sk/menu_sk_sk.rc index e36d1461c..66e89d575 100644 --- a/language/np3_sk_sk/menu_sk_sk.rc +++ b/language/np3_sk_sk/menu_sk_sk.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "Zobraziť &symboly zalomenia\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "Zobraziť &prázdne miesta\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "Zobraziť &konce riadkov\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "Značka histórie zmien" BEGIN diff --git a/language/np3_sv_se/menu_sv_se.rc b/language/np3_sv_se/menu_sv_se.rc index 1add5f311..e5158966b 100644 --- a/language/np3_sv_se/menu_sv_se.rc +++ b/language/np3_sv_se/menu_sv_se.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "Visa radbrytningssymboler\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "Visa blanksteg\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "Visa radslut\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "Markör för ändringshistorik" BEGIN diff --git a/language/np3_tr_tr/menu_tr_tr.rc b/language/np3_tr_tr/menu_tr_tr.rc index 72d8fd46a..8a6b95c84 100644 --- a/language/np3_tr_tr/menu_tr_tr.rc +++ b/language/np3_tr_tr/menu_tr_tr.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "Satır kaydırmaları görüntülensin\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "Boşluklar görüntülensin\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "Satır sonları görüntülensin\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "Değişiklik geçmişi işareti" BEGIN diff --git a/language/np3_vi_vn/menu_vi_vn.rc b/language/np3_vi_vn/menu_vi_vn.rc index 8fca1506d..6137968e1 100644 --- a/language/np3_vi_vn/menu_vi_vn.rc +++ b/language/np3_vi_vn/menu_vi_vn.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "Hiển thị ký hiệu ngắt dòng(&P)\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "Hiển thị khoảng trắng(&B)\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "Hiển thị ký hiệu kết thúc dòng(&E)\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "Thay đổi dấu hiệu thay đổi" BEGIN diff --git a/language/np3_zh_cn/menu_zh_cn.rc b/language/np3_zh_cn/menu_zh_cn.rc index cd2c46177..9f0d54300 100644 --- a/language/np3_zh_cn/menu_zh_cn.rc +++ b/language/np3_zh_cn/menu_zh_cn.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "显示软换行(&P)\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "显示空格(&W)\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "显示换行符号(&E)\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "变更记录标记" BEGIN diff --git a/language/np3_zh_tw/menu_zh_tw.rc b/language/np3_zh_tw/menu_zh_tw.rc index c3a3e0131..fd4d94ae8 100644 --- a/language/np3_zh_tw/menu_zh_tw.rc +++ b/language/np3_zh_tw/menu_zh_tw.rc @@ -352,6 +352,7 @@ BEGIN MENUITEM "顯示軟換行(&P)\tCtrl+Shift+7", IDM_VIEW_WORDWRAPSYMBOLS MENUITEM "顯示空格(&W)\tCtrl+Shift+8", IDM_VIEW_SHOWBLANKS MENUITEM "顯示換行符號(&E)\tCtrl+Shift+9", IDM_VIEW_SHOWEOLS + MENUITEM "Show &Non-Printable Characters", IDM_VIEW_NONPRINTINGCHARS MENUITEM SEPARATOR POPUP "變更記錄標記" BEGIN diff --git a/src/Config/Config.cpp b/src/Config/Config.cpp index fb11c9444..a3e5701a5 100644 --- a/src/Config/Config.cpp +++ b/src/Config/Config.cpp @@ -1788,6 +1788,7 @@ void LoadSettings() GET_BOOL_VALUE_FROM_INISECTION(ViewWhiteSpace, false); GET_BOOL_VALUE_FROM_INISECTION(ViewEOLs, false); + GET_BOOL_VALUE_FROM_INISECTION(ViewNonPrintingChars, false); #ifdef D_NP3_WIN10_DARK_MODE GET_CAST_INT_VALUE_FROM_INISECTION(WIN_DISPL_MODE, WinThemeDarkMode, 0, 0, 2); @@ -2256,6 +2257,7 @@ static bool _SaveSettings(bool bForceSaveSettings) SAVE_VALUE_IF_NOT_EQ_DEFAULT(Bool, MarkOccurrencesCurrentWord); SAVE_VALUE_IF_NOT_EQ_DEFAULT(Bool, ViewWhiteSpace); SAVE_VALUE_IF_NOT_EQ_DEFAULT(Bool, ViewEOLs); + SAVE_VALUE_IF_NOT_EQ_DEFAULT(Bool, ViewNonPrintingChars); // recalculate encoding-dependent defaults before save comparison bool const bCurrentEncUTF8 = (Settings.DefaultEncoding == CPI_UTF8 || Settings.DefaultEncoding == CPI_UTF8SIGN); diff --git a/src/Notepad3.c b/src/Notepad3.c index 843434290..29b077f91 100644 --- a/src/Notepad3.c +++ b/src/Notepad3.c @@ -4668,6 +4668,7 @@ LRESULT MsgInitMenu(HWND hwnd, WPARAM wParam, LPARAM lParam) CheckCmd(hmenu, IDM_VIEW_SHOWBLANKS, Settings.ViewWhiteSpace); CheckCmd(hmenu, IDM_VIEW_SHOWEOLS, Settings.ViewEOLs); + CheckCmd(hmenu, IDM_VIEW_NONPRINTINGCHARS, Settings.ViewNonPrintingChars); CheckCmd(hmenu, IDM_VIEW_WORDWRAPSYMBOLS, Settings.ShowWordWrapSymbols); CheckCmd(hmenu, IDM_VIEW_TITLEBAR, Settings.ShowTitlebar); CheckCmd(hmenu, IDM_VIEW_MENUBAR, Settings.ShowMenubar); @@ -6565,6 +6566,11 @@ static bool _HandleViewAndSettingsCommands(HWND hwnd, UINT umsg, WPARAM wParam, SciCall_SetViewEOL(Settings.ViewEOLs); break; + case IDM_VIEW_NONPRINTINGCHARS: + Settings.ViewNonPrintingChars = !Settings.ViewNonPrintingChars; + Style_SetNonPrintCharRepresentations(Globals.hwndEdit); + break; + case IDM_VIEW_MATCHBRACES: Settings.MatchBraces = !Settings.MatchBraces; if (Settings.MatchBraces) { diff --git a/src/Styles.c b/src/Styles.c index 4f2891b76..619843ca9 100644 --- a/src/Styles.c +++ b/src/Styles.c @@ -1428,6 +1428,87 @@ static __forceinline bool Style_StrHasAttribute(LPCWSTR lpszStyle, LPCWSTR name) } +//============================================================================= +// +// Style_SetNonPrintCharRepresentations() +// +// Install / clear visible-abbreviation representations for non-printable and +// confusable Unicode formatting characters via Scintilla's representation API. +// Keyed by UTF-8 bytes (Notepad3 always uses SC_CP_UTF8 internally). +// +static const struct { + const char* utf8; + const char* abbr; +} s_NonPrintCharRepresentations[] = { + { "\xC2\x85", "NEL" }, // U+0085 next line + { "\xC2\xA0", "NBSP" }, // U+00A0 no-break space + { "\xD8\x9C", "ALM" }, // U+061C arabic letter mark + { "\xE1\x9A\x80", "OSPM" }, // U+1680 ogham space mark + { "\xE1\xA0\x8E", "MVS" }, // U+180E mongolian vowel separator + { "\xE2\x80\x80", "NQSP" }, // U+2000 en quad + { "\xE2\x80\x81", "MQSP" }, // U+2001 em quad + { "\xE2\x80\x82", "ENSP" }, // U+2002 en space + { "\xE2\x80\x83", "EMSP" }, // U+2003 em space + { "\xE2\x80\x84", "3/MSP" }, // U+2004 three-per-em space + { "\xE2\x80\x85", "4/MSP" }, // U+2005 four-per-em space + { "\xE2\x80\x86", "6/MSP" }, // U+2006 six-per-em space + { "\xE2\x80\x87", "FSP" }, // U+2007 figure space + { "\xE2\x80\x88", "PSP" }, // U+2008 punctuation space + { "\xE2\x80\x89", "THSP" }, // U+2009 thin space + { "\xE2\x80\x8A", "HSP" }, // U+200A hair space + { "\xE2\x80\x8B", "ZWSP" }, // U+200B zero-width space + { "\xE2\x80\x8C", "ZWNJ" }, // U+200C zero-width non-joiner + { "\xE2\x80\x8D", "ZWJ" }, // U+200D zero-width joiner + { "\xE2\x80\x8E", "LRM" }, // U+200E left-to-right mark + { "\xE2\x80\x8F", "RLM" }, // U+200F right-to-left mark + { "\xE2\x80\xA8", "LS" }, // U+2028 line separator + { "\xE2\x80\xA9", "PS" }, // U+2029 paragraph separator + { "\xE2\x80\xAA", "LRE" }, // U+202A left-to-right embedding + { "\xE2\x80\xAB", "RLE" }, // U+202B right-to-left embedding + { "\xE2\x80\xAC", "PDF" }, // U+202C pop directional formatting + { "\xE2\x80\xAD", "LRO" }, // U+202D left-to-right override + { "\xE2\x80\xAE", "RLO" }, // U+202E right-to-left override + { "\xE2\x80\xAF", "NNBSP" }, // U+202F narrow no-break space + { "\xE2\x81\x9F", "MMSP" }, // U+205F medium mathematical space + { "\xE2\x81\xA0", "WJ" }, // U+2060 word joiner + { "\xE2\x81\xA6", "LRI" }, // U+2066 left-to-right isolate + { "\xE2\x81\xA7", "RLI" }, // U+2067 right-to-left isolate + { "\xE2\x81\xA8", "FSI" }, // U+2068 first strong isolate + { "\xE2\x81\xA9", "PDI" }, // U+2069 pop directional isolate + { "\xE2\x81\xAA", "ISS" }, // U+206A inhibit symmetric swapping + { "\xE2\x81\xAB", "ASS" }, // U+206B activate symmetric swapping + { "\xE2\x81\xAC", "IAFS" }, // U+206C inhibit arabic form shaping + { "\xE2\x81\xAD", "AAFS" }, // U+206D activate arabic form shaping + { "\xE2\x81\xAE", "NADS" }, // U+206E national digit shapes + { "\xE2\x81\xAF", "NODS" }, // U+206F nominal digit shapes + { "\xE3\x80\x80", "IDSP" }, // U+3000 ideographic space + { "\xEF\xBB\xBF", "ZWNBSP" }, // U+FEFF zero-width no-break space / BOM +}; + +void Style_SetNonPrintCharRepresentations(HWND hwnd) +{ + UNREFERENCED_PARAMETER(hwnd); + + // GDI rendering cannot draw representations -> always clear + bool const bEnable = Settings.ViewNonPrintingChars && + (SciCall_GetTechnology() != SC_TECHNOLOGY_DEFAULT); + + int const appearance = SC_REPRESENTATION_BLOB | SC_REPRESENTATION_COLOUR; + COLORALPHAREF const argb = SciCall_GetElementColour(SC_ELEMENT_WHITE_SPACE); + + for (size_t i = 0; i < COUNTOF(s_NonPrintCharRepresentations); ++i) { + const char* const utf8 = s_NonPrintCharRepresentations[i].utf8; + if (bEnable) { + SciCall_SetRepresentation(utf8, s_NonPrintCharRepresentations[i].abbr); + SciCall_SetRepresentationAppearance(utf8, appearance); + SciCall_SetRepresentationColour(utf8, argb); + } else { + SciCall_ClearRepresentation(utf8); + } + } +} + + //============================================================================= // // Style_SetLexer() @@ -1793,6 +1874,9 @@ void Style_SetLexer(HWND hwnd, PEDITLEXER pLexNew) SciCall_SetRepresentationColour("\r\n", SciCall_GetElementColour(SC_ELEMENT_WHITE_SPACE)); } + // non-printable / formatting Unicode characters (NBSP, ZWSP, LRM, ...) + Style_SetNonPrintCharRepresentations(hwnd); + rgb = RGB(0, 0, 0); rgbWrt = rgb; if (Style_StrGetColor(pCurrentStandard->Styles[STY_WHITESPACE].szValue, BACKGROUND_LAYER, &rgb, &rgbWrt, true)) { diff --git a/src/Styles.h b/src/Styles.h index b661e3fd3..bdc683cbc 100644 --- a/src/Styles.h +++ b/src/Styles.h @@ -60,6 +60,7 @@ float Style_GetCurrentLexerFontSize(); void Style_SetLexer(HWND hwnd,PEDITLEXER pLexNew); void Style_FillRelatedStyles(HWND hwnd, const PEDITLEXER pLexer); void Style_SetUrlHotSpot(HWND hwnd); +void Style_SetNonPrintCharRepresentations(HWND hwnd); void Style_SetInvisible(HWND hwnd, bool); //void Style_SetReadonly(HWND hwnd, bool); void Style_HighlightCurrentLine(HWND hwnd, int); diff --git a/src/TypeDefs.h b/src/TypeDefs.h index 7f3496175..7d9d06afa 100644 --- a/src/TypeDefs.h +++ b/src/TypeDefs.h @@ -674,6 +674,7 @@ typedef struct SETTINGS_T { bool MarkOccurrencesCurrentWord; bool ViewWhiteSpace; bool ViewEOLs; + bool ViewNonPrintingChars; cpi_enc_t DefaultEncoding; // default new file encoding bool UseDefaultForFileEncoding; bool LoadASCIIasUTF8; diff --git a/test/test_files/non_printable_chars.txt b/test/test_files/non_printable_chars.txt new file mode 100644 index 000000000..250139c62 --- /dev/null +++ b/test/test_files/non_printable_chars.txt @@ -0,0 +1,69 @@ +Notepad3 - Test file for "Show Non-Printable Characters" (View menu) +==================================================================== + +How to read this file +--------------------- +Each row below frames one non-printable Unicode character between '>' and '<'. + + - With "View > Show Non-Printable Characters" OFF, the area between the + markers looks empty or like ordinary whitespace. + - With it ON (and a non-GDI rendering technology), the abbreviation listed + in the third column appears inside a coloured rounded box between '>' and + '<'. + +Note: U+2028 (LS) and U+2029 (PS) are paragraph-breaking characters in some +renderers; in Notepad3 they are represented inline with their abbreviation. + +Table +----- +Codepoint Name Abbreviation Sample +--------- ----------------------------------- ------------ -------- +U+0085 next line NEL >…< +U+00A0 no-break space NBSP > < +U+061C arabic letter mark ALM >؜< +U+1680 ogham space mark OSPM > < +U+180E mongolian vowel separator MVS >᠎< +U+2000 en quad NQSP > < +U+2001 em quad MQSP > < +U+2002 en space ENSP > < +U+2003 em space EMSP > < +U+2004 three-per-em space 3/MSP > < +U+2005 four-per-em space 4/MSP > < +U+2006 six-per-em space 6/MSP > < +U+2007 figure space FSP > < +U+2008 punctuation space PSP > < +U+2009 thin space THSP > < +U+200A hair space HSP > < +U+200B zero-width space ZWSP >​< +U+200C zero-width non-joiner ZWNJ >‌< +U+200D zero-width joiner ZWJ >‍< +U+200E left-to-right mark LRM >‎< +U+200F right-to-left mark RLM >‏< +U+2028 line separator LS >
< +U+2029 paragraph separator PS >
< +U+202A left-to-right embedding LRE >‪< +U+202B right-to-left embedding RLE >‫< +U+202C pop directional formatting PDF >‬< +U+202D left-to-right override LRO >‭< +U+202E right-to-left override RLO >‮< +U+202F narrow no-break space NNBSP > < +U+205F medium mathematical space MMSP > < +U+2060 word joiner WJ >⁠< +U+2066 left-to-right isolate LRI >⁦< +U+2067 right-to-left isolate RLI >⁧< +U+2068 first strong isolate FSI >⁨< +U+2069 pop directional isolate PDI >⁩< +U+206A inhibit symmetric swapping ISS >< +U+206B activate symmetric swapping ASS >< +U+206C inhibit arabic form shaping IAFS >< +U+206D activate arabic form shaping AAFS >< +U+206E national digit shapes NADS >< +U+206F nominal digit shapes NODS >< +U+3000 ideographic space IDSP > < +U+FEFF zero-width no-break space / BOM ZWNBSP >< + +Free-form mix (paste into a longer paragraph to spot stray markers): + +word1 word2​joined‎LTR‏RTL narrow matheof + +--- end of test file --- diff --git a/todo/TODO.md b/todo/TODO.md index 295aae2f9..7d1551a97 100644 --- a/todo/TODO.md +++ b/todo/TODO.md @@ -26,8 +26,9 @@ - [x] **(Q1) BUG: /m command line uses last search mode** - ✅ FIXED - Issue: [#5060](https://github.com/rizonesoft/Notepad3/issues/5060) - Fix: When `/m` is used without 'R' flag, explicitly clear SCFIND_REGEXP to force text mode -- [x] **(Q3) Replace GetOpenFileNameW with IFileOpenDialog** - Modern file dialog API - - Issues: [#5066](https://github.com/rizonesoft/Notepad3/issues/5066), [#5080](https://github.com/rizonesoft/Notepad3/issues/5080) +- [x] **(Q3) Replace GetOpenFileNameW with IFileOpenDialog** - Modern file dialog API - ✅ IMPLEMENTED + - Issue: [#5066](https://github.com/rizonesoft/Notepad3/issues/5066) + - Issue: [#5080](https://github.com/rizonesoft/Notepad3/issues/5080) - Fixes crash on Windows Server 2022 (STATUS_STACK_BUFFER_OVERRUN in ntdll.dll) - See [research/server2022-file-dialog-crash.md](research/server2022-file-dialog-crash.md) - [ ] **Test on Windows Server 2022 and higer** - ⚠ Validation❗ @@ -37,7 +38,7 @@ - [ ] **(Q2) BUG: Replace dialog full-width caching** - Second replace uses wrong character - Issue: [#4268](https://github.com/rizonesoft/Notepad3/issues/4268) - CJK full-width replacement cached incorrectly -- [x] **(Q2) BUG: Initial window position not working** - Position settings ignored - ✅ FIXED +- [x] **(Q2) BUG: Initial window position not working** - Position settings ignored - ✅ IMPLEMENTED - Issue: [#4725](https://github.com/rizonesoft/Notepad3/issues/4725) - [ ] **To be analyzed - works as designed ???** - ⚠ Validation ❗ - [ ] **(Q2) BUG: Minipath options don't save** - FullRowSelect/TrackSelect broken @@ -60,9 +61,9 @@ - [x] **(Q1) BUG: Mouse scroll settings not updated** - ✅ FIXED - Issue: [#5223](https://github.com/rizonesoft/Notepad3/issues/5223) - Fix: Forward `WM_SETTINGCHANGE` to Scintilla to refresh cached scroll parameters -- [x] **(Q2) BUG: File lock held too long on save** - Blocks FileSystemWatcher - ✅ FIXED +- [x] **(Q2) BUG: File lock held too long on save** - Blocks FileSystemWatcher - ✅ IMPLEMENTED - Issue: [#5301](https://github.com/rizonesoft/Notepad3/issues/5301) - ⚠ Validation❗ -- [x] **(Q2) BUG: Folder handle leak** - Can't rename/delete folders with opened files +- [x] **(Q2) BUG: Folder handle leak** - Can't rename/delete folders with opened files - ✅ IMPLEMENTED - Issue: [#5342](https://github.com/rizonesoft/Notepad3/issues/5342) - ⚠ Validation❗ - [x] **(Q1) BUG: Black line in Language menu** - ✅ FIXED - Issue: [#5361](https://github.com/rizonesoft/Notepad3/issues/5361) @@ -133,16 +134,16 @@ - Issue: [#3580](https://github.com/rizonesoft/Notepad3/issues/3580) - ⚠ Validation ❗ - [ ] **(Q3) Additional Syntax Highlighting** - New language lexers - - Haskell: [#3035](https://github.com/rizonesoft/Notepad3/issues/3035) - Lexilla `LexHaskell.cxx` - - Racket: [#3035](https://github.com/rizonesoft/Notepad3/issues/3035) - Could use Lisp/Scheme lexer - - OpenCL: [#5374](https://github.com/rizonesoft/Notepad3/issues/5374) - C-like with extra keywords - - Verilog HDL: [#4108](https://github.com/rizonesoft/Notepad3/issues/4108) - Lexilla `LexVerilog.cxx` - - [x] JSON5: [#5411](https://github.com/rizonesoft/Notepad3/issues/5411) - Extend JSON lexer - ⚠ Validation ❗ - - SourcePawn: [#5430](https://github.com/rizonesoft/Notepad3/issues/5430) - SourceMod scripting - - Groovy: [#5093](https://github.com/rizonesoft/Notepad3/issues/5093) - - Swift, Zig, Scala, F#, WASM, Vim, OCaml, Smali, GraphViz, Rebol + - [ ] Haskell: [#3035](https://github.com/rizonesoft/Notepad3/issues/3035) - Lexilla `LexHaskell.cxx` + - [ ] Racket: [#3035](https://github.com/rizonesoft/Notepad3/issues/3035) - Could use Lisp/Scheme lexer + - [ ] OpenCL: [#5374](https://github.com/rizonesoft/Notepad3/issues/5374) - C-like with extra keywords + - [ ] Verilog HDL: [#4108](https://github.com/rizonesoft/Notepad3/issues/4108) - Lexilla `LexVerilog.cxx` + - [x] JSON5: [#5411](https://github.com/rizonesoft/Notepad3/issues/5411) - ⚠ Validation ❗ + - [ ] SourcePawn: [#5430](https://github.com/rizonesoft/Notepad3/issues/5430) - SourceMod scripting + - [ ] Groovy: [#5093](https://github.com/rizonesoft/Notepad3/issues/5093) + - [ ] Swift, Zig, Scala, F#, WASM, Vim, OCaml, Smali, GraphViz, Rebol - (Maybe) CSS in `