Merge pull request #5819 from RaiKoHoff/dev_master

feat: matching braces (Ctrl+B and with selection Ctrl+Shift+B) inside blocks
This commit is contained in:
Rainer Kottenhoff 2026-05-05 14:42:37 +02:00 committed by GitHub
commit 52b5efa668
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 194 additions and 5 deletions

View File

@ -267,6 +267,8 @@
#define IDS_MUI_URL_DIR_EXISTS 15065
#define IDS_MUI_URL_FILE_EXISTS 15066
#define IDS_MUI_URL_PATH_NOT_FOUND 15067
#define IDS_MUI_UNMATCHED_BRACE 15068
#define IDS_MUI_NO_ENCLOSING_BRACE 15069
#define IDS_MUI_SB_MARK_ALL_OCC 15500
#define IDS_MUI_SB_TOGGLE_VIEW 15501

View File

@ -208,6 +208,8 @@ BEGIN
"URL gespesifiseerde pad nie gevind nie!"
IDS_MUI_URL_OPEN_FILE "\nAlt + Klik om die lêer oop te maak"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + Klik om skakel in leser oop te maak"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"Die behoud van die oorspronklike tydstempel vir lêerveranderings is geaktiveer\nHierdie opsie bly vir hierdie sessie!"
IDS_MUI_OUT_OFF_OCCMRK "Die optredemarkers raak op (Bookmerk/Merk)!"

View File

@ -208,6 +208,8 @@ BEGIN
"Азначаны ва URL шлях не знойдзены!"
IDS_MUI_URL_OPEN_FILE "\nAlt + Click, каб адкрыць файл"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + Click, каб адкрыць спасылку ў браўзеры"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"Захоўванне зыходняй часаадзнакі змянення файла ўключана\nГэта опцыя застанецца ўключанай для гэтай сесіі!"
IDS_MUI_OUT_OFF_OCCMRK "Маркеры ўваходжанняў (закладка/падсвечванне) на зыходзе!"

View File

@ -208,6 +208,8 @@ BEGIN
"URL spezifizierter Pfad nicht gefunden!"
IDS_MUI_URL_OPEN_FILE "\nAlt + Click um die Datei zu öffnen"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + Click um die URL im Browser zu öffnen"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"Die Speicherung mit originalem Zeitstempel wurde aktiviert\nDiese Option gilt nun für die gesamte Sitzung (Datei)!"
IDS_MUI_OUT_OFF_OCCMRK "Es stehen keine weiteren Fundstellen Markierungen/Lesezeichen mehr zur Verfügung!"

View File

@ -208,6 +208,8 @@ BEGIN
"Η καθορισμένη διαδρομή URL δεν βρέθηκε!"
IDS_MUI_URL_OPEN_FILE "\nAlt + Κλικ για άνοιγμα του αρχείου"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + Κλικ για άνοιγμα του συνδέσμου στον περιηγητή"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"Η διατήρηση της αρχικής χρονοσήμανσης μεταβολής του αρχείου είναι ενεργοποιημένη\nΑυτή η επιλογή θα παραμείνει για αυτή την σύνοδο!"
IDS_MUI_OUT_OFF_OCCMRK "Δεν υπάρχουν άλλοι δείκτες εμφάνισης (Σελιδοδείκτης/Επισήμανση)!"

View File

@ -208,6 +208,8 @@ BEGIN
"URL specified path not found!"
IDS_MUI_URL_OPEN_FILE "\nAlt + Click to open the file"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + Click to open link in browser"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"Preserving original File Modification Timestamp enabled\nThis option will stay for this session!"
IDS_MUI_OUT_OFF_OCCMRK "Running out of Occurrence Markers (Bookmark/Highlight)!"

View File

@ -208,6 +208,8 @@ BEGIN
"URL specified path not found!"
IDS_MUI_URL_OPEN_FILE "\nAlt + Click to open the file"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + Click to open link in browser"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"Preserving original File Modification Timestamp enabled\nThis option will stay for this session!"
IDS_MUI_OUT_OFF_OCCMRK "Running out of Occurrence Markers (Bookmark/Highlight)!"

View File

@ -208,6 +208,8 @@ BEGIN
"¡Ruta especificada por URL no encontrada!"
IDS_MUI_URL_OPEN_FILE "\nAlt + Cliquear para abrir el archivo"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + Cliquear para abrir el enlace en navegador"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"Conservar la marca de tiempo de modificación del archivo original habilitada\n¡Esta opción permanecerá para esta sesión!"
IDS_MUI_OUT_OFF_OCCMRK "¡No hay más marcadores de ocurrencia (Marcador/Resaltar)!"

View File

@ -208,6 +208,8 @@ BEGIN
"URL määriteltyä polkua ei löydy!"
IDS_MUI_URL_OPEN_FILE "\nAlt + Avaa tiedosto klikkaamalla"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + Avaa linkki selaimessa klikkaamalla"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"Alkuperäisen tiedoston muokkausaikaleiman säilyttäminen käytössä\nVaihtoehto säilyy tämän istunnon ajan!"
IDS_MUI_OUT_OFF_OCCMRK "Esiintymämerkit loppumassa (kirjanmerkki/korostus)!"

View File

@ -208,6 +208,8 @@ BEGIN
"Chemin spécifié par l'URL introuvable !"
IDS_MUI_URL_OPEN_FILE "\nAlt + Clic pour ouvrir le fichier"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + Clic pour ouvrir le lien dans le navigateur"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"Préservation de l'horodatage de modification original du fichier activée\nCette option restera active pour cette session !"
IDS_MUI_OUT_OFF_OCCMRK "Il n'y a pas d'autres marqueurs d'occurrence (Signet/Surlignage) !"

View File

@ -208,6 +208,8 @@ BEGIN
"URL निर्दिष्ट स्थान मौजूद नहीं है!"
IDS_MUI_URL_OPEN_FILE "\nAlt + फ़ाईल खोलने के लिए क्लिक करें"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + ब्राउज़र में लिंक खोलने के लिए क्लिक करें"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"फ़ाईल परिवर्तन का मौलिक समय संरक्षण सक्षम है\nयह विकल्प इस सत्र के लिए बना रहेगा!"
IDS_MUI_OUT_OFF_OCCMRK "उपस्तिथि चिन्हक खत्म हो रहे है (बुकमार्क/हाइलाइट)!"

View File

@ -208,6 +208,8 @@ BEGIN
"Az URL-ben megadott útvonal nem létezik!"
IDS_MUI_URL_OPEN_FILE "\nAlt + Kattintás a fájl megnyitásához"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + Kattintás a böngészőben való megnyitáshoz"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"Az eredeti fájl módosítási idejeének megőrzése opció be van kapcsolva\nA beállítás így marad erre a munkamenetre!"
IDS_MUI_OUT_OFF_OCCMRK "Elfogytak a találatjelölők (Könyvjelző/Kiemelés)!"

View File

@ -208,6 +208,8 @@ BEGIN
"Jalur URL yang dituju tidak ditemukan!"
IDS_MUI_URL_OPEN_FILE "\nAlt + Klik untuk membuka berkas"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + Klik untuk membuka tautan di browser"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"Mempertahankan berkas dari Modifikasi Stempel Waktu telah diaktifkan\nOpsi ini hanya akan berlangsung untuk sesi ini saja!"
IDS_MUI_OUT_OFF_OCCMRK "Penanda Pengulangan Kata telah habis (Markah/Sorotan)!"

View File

@ -208,6 +208,8 @@ BEGIN
"Il percorso specificato nella URL non è stato trovato!"
IDS_MUI_URL_OPEN_FILE "\nAlt + Click per aprire il file"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + Click per aprire il link nel browser"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"Conservazione della data di ultima modifica originale abilitata\nQuesta opzione rimarrà attiva per questa sessione!"
IDS_MUI_OUT_OFF_OCCMRK "Marcatori occorrenza in esaurimento (Segnalibro/evidenziatura)!"

View File

@ -208,6 +208,8 @@ BEGIN
"URL で指定されたパスが見つかりません!"
IDS_MUI_URL_OPEN_FILE "\nAlt + クリック : ファイルを開きます"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + クリック : ブラウザーでリンクを開きます"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"元ファイルの更新日時のままにします\nこのセッションでのみ有効となります"
IDS_MUI_OUT_OFF_OCCMRK "単語出現マーカーが枯渇しています (しおり/強調表示)!"

View File

@ -208,6 +208,8 @@ BEGIN
"URL 지정 경로를 찾을 수 없습니다!"
IDS_MUI_URL_OPEN_FILE "\nAlt+클릭하여 파일을 엽니다"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl+클릭하여 탐색기에서 링크를 엽니다"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"원래 파일 수정 타임스탬프를 보존할 수 있습니다\n이 옵션은 이 세션에 유지됩니다!"
IDS_MUI_OUT_OFF_OCCMRK "발생 마커 (북마크/강조)가 부족합니다!"

View File

@ -208,6 +208,8 @@ BEGIN
"URL opgegeven pad niet gevonden!"
IDS_MUI_URL_OPEN_FILE "\nAlt + Klik om het bestand te openen"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + Klik om de link in de browser te openen"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"Tijdstempel originele bestandswijziging behouden\nDeze optie blijft gedurende deze sessie actief!"
IDS_MUI_OUT_OFF_OCCMRK "Er zijn geen andere markeringen van voorvallen (Bladwijzer/Markering)!"

View File

@ -208,6 +208,8 @@ BEGIN
"URL nie znaleziono określonej ścieżki!"
IDS_MUI_URL_OPEN_FILE "\nAlt + Kliknij, aby otworzyć plik"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + Kliknij, aby otworzyć link w przeglądarce"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"Włączono zachowanie znacznika czasu w modyfikacji pliku\nTa opcja pozostanie dla tej sesji!"
IDS_MUI_OUT_OFF_OCCMRK "Kończą się znaczniki występowania (Zakładka/Podświetlenie)!"

View File

@ -208,6 +208,8 @@ BEGIN
"Caminho especificado pelo URL não encontrado!"
IDS_MUI_URL_OPEN_FILE "\nAlt + Clique para abrir o arquivo"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + Clique para abrir o link no navegador"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"Preservação do Timestamp original de Modificação de Arquivo habilitado\nEsta opção ficará ativa durante esta sessão!"
IDS_MUI_OUT_OFF_OCCMRK "Ficando sem marcadores de ocorrência (Marcador/Destacado)!"

View File

@ -208,6 +208,8 @@ BEGIN
"Caminho especificado da URL não encontrado!"
IDS_MUI_URL_OPEN_FILE "\nAlt + Clique para abrir o ficheiro"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + Clique para abrir a ligação no navegador"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"Manter a data/hora de modificação original do ficheiro activada\nEsta opção será válida para esta sessão!"
IDS_MUI_OUT_OFF_OCCMRK "Não existem outros marcadores de ocorrência (Marcador/Realce)!"

View File

@ -208,6 +208,8 @@ BEGIN
"Указанный в URL путь не найден!"
IDS_MUI_URL_OPEN_FILE "\nAlt + Click, чтобы открыть файл"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + Click, чтобы открыть ссылку в браузере"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"Сохранение исходной метки времени изменения файла включено\nЭта опция останется включенной для текущей сессии!"
IDS_MUI_OUT_OFF_OCCMRK "Маркеры вхождения (закладка/отметка) на исходе!"

View File

@ -208,6 +208,8 @@ BEGIN
"URL zadaná cesta nebola nájdená!"
IDS_MUI_URL_OPEN_FILE "\nAlt + kliknutím otvoríte súbor"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + kliknutím otvoríte prepojenie"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"Zachovanie pôvodnej časovej pečiatky pri úprave súboru je povolené\nTáto možnosť zostane len pre túto reláciu!"
IDS_MUI_OUT_OFF_OCCMRK "Dochádzajú označené výskyty (záložka/zvýraznenie)!"

View File

@ -208,6 +208,8 @@ BEGIN
"Angiven URL-sökväg existerar redan!"
IDS_MUI_URL_OPEN_FILE "\nAlt + Klicka för att öppna fil"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + Klicka för att öppna länken i webbläsaren"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"Bevarande av tidsstämpel för filmodifiering är aktiverad\nInställningen är giltig för denna session!"
IDS_MUI_OUT_OFF_OCCMRK "Inga fler markeringar (bokmärke/markering)!"

View File

@ -208,6 +208,8 @@ BEGIN
"İnternet adresinde belirtilen yol bulunamadı!"
IDS_MUI_URL_OPEN_FILE "\nAlt + Dosyayı açmak için tıklayın"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl + Bağlantıyı tarayıcıda açmak için tıklayın"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"Özgün dosya değişiklik zamanının korunması seçeneği etkinleştirilmiş\nBu seçenek bu oturum boyunca etkin olacak!"
IDS_MUI_OUT_OFF_OCCMRK "Yinelenme işaretleyicileri bitiyor (Yer imi/Vurgula)!"

View File

@ -208,6 +208,8 @@ BEGIN
"Không thể tìm thấy đường dẫn URL đã chỉ định!"
IDS_MUI_URL_OPEN_FILE "\nAlt+Click để mở tệp"
IDS_MUI_URL_OPEN_BROWSER "\nCtrl+Click để mở liên kết trong trình duyệt"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"Có thể bảo tồn dấu thời gian sửa đổi tệp gốc\nTùy chọn này sẽ giữ nguyên trong phiên này!"
IDS_MUI_OUT_OFF_OCCMRK "Không đủ dấu đánh dấu (dấu trang/nổi bật)!"

View File

@ -208,6 +208,8 @@ BEGIN
"URL 指定的路径不存在!"
IDS_MUI_URL_OPEN_FILE "\n按住 Alt 同时点击以打开文件"
IDS_MUI_URL_OPEN_BROWSER "\n按住 Ctrl 同时点击以在浏览器中打开链接"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"已启用维持文件最后修改时间功能\n\n本选项将在当前会话期间生效"
IDS_MUI_OUT_OFF_OCCMRK "书签/高亮标记已用完!"

View File

@ -208,6 +208,8 @@ BEGIN
"找不到 URL 指定路徑!"
IDS_MUI_URL_OPEN_FILE "\n按住 Alt 同時點選以開啟檔案"
IDS_MUI_URL_OPEN_BROWSER "\n按住 Ctrl 同時點選以在瀏覽器中開啟連結"
IDS_MUI_UNMATCHED_BRACE "Unmatched Brace"
IDS_MUI_NO_ENCLOSING_BRACE "No Enclosing Brace"
IDS_MUI_INF_PRSVFILEMODTM
"已啟用維持檔案最後修改時間功能\n\n本選項將在目前工作階段期間生效"
IDS_MUI_OUT_OFF_OCCMRK "書籤/高亮標記已用完!"

View File

@ -238,8 +238,8 @@ A complete reference for the shortcuts wired into Notepad3 (`src/Notepad3.rc` ac
| `F4` | Replace next | NP2 |
| `Ctrl+G` | Goto line / column dialog | NP2 |
| `Ctrl+Shift+F` | Search in files (grepWin) | (orig-NP2: "Recode as default") |
| `Ctrl+B` | Find matching brace | NP2 |
| `Ctrl+Shift+B` | Select to matching brace | NP2 |
| `Ctrl+B` | **Find matching brace** — if the caret is at a brace `()[]{}` jump to its match; if not at a brace, jump to the nearest enclosing opening brace. If a brace-block selection is active, the caret toggles between the two brace endpoints while preserving the selection. Shows a calltip if no match or no enclosing brace exists. | NP2+ |
| `Ctrl+Shift+B` | **Select to matching brace** — selects the brace pair enclosing the caret (inclusive of both braces). Repeated presses expand the selection outward one nesting level at a time. Pressing when the caret is at the closing brace of the current selection pivots the caret to the opening brace (same selection, reversed direction); pressing again when at the opening brace expands to the next outer pair. Shows a calltip if no match or no enclosing brace exists. | NP2+ |
| `Ctrl+Alt+F2` | Expand selection to next match | (orig-NP2: `F2`) |
| `Ctrl+Alt+Shift+F2` | Expand selection to previous match | (orig-NP2: `Shift+F2`) |

View File

@ -2622,6 +2622,45 @@ void EditHex2Char(HWND hwnd)
}
//=============================================================================
//
// _FindEnclosingOpenBrace() - scan backwards to find the unmatched opening
// brace that encloses the given position. Returns brace position or -1.
//
static DocPos _FindEnclosingOpenBrace(DocPos iStartPos)
{
int depthParen = 0; // ()
int depthBracket = 0; // []
int depthBrace = 0; // {}
DocPos iPos = iStartPos;
while (iPos > 0) {
iPos = SciCall_PositionBefore(iPos);
const char ch = SciCall_GetCharAt(iPos);
switch (ch) {
case ')': ++depthParen; break;
case ']': ++depthBracket; break;
case '}': ++depthBrace; break;
case '(':
if (depthParen == 0) { return iPos; }
--depthParen;
break;
case '[':
if (depthBracket == 0) { return iPos; }
--depthBracket;
break;
case '{':
if (depthBrace == 0) { return iPos; }
--depthBrace;
break;
default:
break;
}
}
return (DocPos)-1;
}
//=============================================================================
//
// EditFindMatchingBrace()
@ -2629,22 +2668,55 @@ void EditHex2Char(HWND hwnd)
void EditFindMatchingBrace()
{
bool bIsAfter = false;
DocPos iBracePos = (DocPos)-1;
DocPos iMatchingBracePos = (DocPos)-1;
const DocPos iCurPos = SciCall_GetCurrentPos();
const char c = SciCall_GetCharAt(iCurPos);
if (StrChrA(NP3_BRACES_TO_MATCH, c)) {
iBracePos = iCurPos;
iMatchingBracePos = SciCall_BraceMatch(iCurPos);
} else { // Try one before
const DocPos iPosBefore = SciCall_PositionBefore(iCurPos);
const char cb = SciCall_GetCharAt(iPosBefore);
if (StrChrA(NP3_BRACES_TO_MATCH, cb)) {
iBracePos = iPosBefore;
iMatchingBracePos = SciCall_BraceMatch(iPosBefore);
}
bIsAfter = true;
}
if (iMatchingBracePos != (DocPos)-1) {
iMatchingBracePos = bIsAfter ? iMatchingBracePos : SciCall_PositionAfter(iMatchingBracePos);
Sci_GotoPosChooseCaret(iMatchingBracePos);
if (SciCall_GetSelectionStart() != SciCall_GetSelectionEnd()) {
// Preserve the selection: pivot it so the caret moves to the matching
// brace while the old caret position becomes the new anchor.
UndoTransActionBegin();
EditSetSelectionEx(iCurPos, iMatchingBracePos, -1, -1);
EndUndoTransAction();
} else {
Sci_GotoPosChooseCaret(iMatchingBracePos);
}
} else if (iBracePos != (DocPos)-1) {
// At a brace but no match — orphan brace
SciCall_BraceBadLight(iBracePos);
ShowBraceMatchCallTip(iBracePos, IDS_MUI_UNMATCHED_BRACE);
} else {
// Not at a brace — search backwards for enclosing opener
const DocPos iEnclosing = _FindEnclosingOpenBrace(iCurPos);
if (iEnclosing != (DocPos)-1) {
const DocPos iMatch = SciCall_BraceMatch(iEnclosing);
if (iMatch != (DocPos)-1) {
Sci_GotoPosChooseCaret(iEnclosing);
} else {
// Enclosing opener has no match — orphan
SciCall_BraceBadLight(iEnclosing);
ShowBraceMatchCallTip(iEnclosing, IDS_MUI_UNMATCHED_BRACE);
Sci_GotoPosChooseCaret(iEnclosing);
}
} else {
// No enclosing brace found at all
ShowBraceMatchCallTip(iCurPos, IDS_MUI_NO_ENCLOSING_BRACE);
}
}
}
@ -2656,21 +2728,32 @@ void EditFindMatchingBrace()
void EditSelectToMatchingBrace()
{
bool bIsAfter = false;
DocPos iBracePos = (DocPos)-1;
DocPos iMatchingBracePos = -1;
const DocPos iCurPos = SciCall_GetCurrentPos();
const char c = SciCall_GetCharAt(iCurPos);
if (StrChrA(NP3_BRACES_TO_MATCH, c)) {
iBracePos = iCurPos;
iMatchingBracePos = SciCall_BraceMatch(iCurPos);
} else { // Try one before
const DocPos iPosBefore = SciCall_PositionBefore(iCurPos);
const char cb = SciCall_GetCharAt(iPosBefore);
if (StrChrA(NP3_BRACES_TO_MATCH, cb)) {
iBracePos = iPosBefore;
iMatchingBracePos = SciCall_BraceMatch(iPosBefore);
}
bIsAfter = true;
}
if (iMatchingBracePos != (DocPos)-1) {
// When the caret sits on an opening brace that is also the start of an existing
// selection, the user wants to expand outward to the next enclosing pair — not
// toggle the anchor to the closing brace.
const bool bIsOpeningBrace = (c == '(' || c == '[' || c == '{');
const bool bSelStartsHere = (SciCall_GetSelectionStart() == iCurPos) &&
(SciCall_GetSelectionEnd() != iCurPos);
const bool bExpandOutward = (!bIsAfter && bIsOpeningBrace && bSelStartsHere);
if (iMatchingBracePos != (DocPos)-1 && !bExpandOutward) {
UndoTransActionBegin();
if (bIsAfter) {
EditSetSelectionEx(iCurPos, iMatchingBracePos, -1, -1);
@ -2678,6 +2761,37 @@ void EditSelectToMatchingBrace()
EditSetSelectionEx(iCurPos, SciCall_PositionAfter(iMatchingBracePos), -1, -1);
}
EndUndoTransAction();
} else if (iBracePos != (DocPos)-1 && !bExpandOutward) {
// At a brace but no match — orphan brace
SciCall_BraceBadLight(iBracePos);
ShowBraceMatchCallTip(iBracePos, IDS_MUI_UNMATCHED_BRACE);
} else {
// Find enclosing brace pair and select (or expand outward past current selection)
const DocPos iSelStart = SciCall_GetSelectionStart();
const DocPos iSelEnd = SciCall_GetSelectionEnd();
const bool bHasSelection = (iSelStart != iSelEnd);
DocPos iSearchFrom = bHasSelection ? iSelStart : iCurPos;
const DocPos iEnclosing = _FindEnclosingOpenBrace(iSearchFrom);
if (iEnclosing != (DocPos)-1) {
const DocPos iMatch = SciCall_BraceMatch(iEnclosing);
if (iMatch != (DocPos)-1) {
// Ensure the match actually encloses the selection/cursor
if (!bHasSelection || iMatch >= iSelEnd) {
UndoTransActionBegin();
EditSetSelectionEx(iEnclosing, SciCall_PositionAfter(iMatch), -1, -1);
EndUndoTransAction();
}
} else {
// Enclosing opener has no match — orphan
SciCall_BraceBadLight(iEnclosing);
ShowBraceMatchCallTip(iEnclosing, IDS_MUI_UNMATCHED_BRACE);
}
} else {
// No enclosing brace found at all
ShowBraceMatchCallTip(iCurPos, IDS_MUI_NO_ENCLOSING_BRACE);
}
}
}

View File

@ -12462,6 +12462,26 @@ void ShowWrapAroundCallTip(bool forwardSearch)
}
//=============================================================================
//
// ShowBraceMatchCallTip()
//
void ShowBraceMatchCallTip(DocPos pos, UINT idsMsg)
{
static char chToolTip[80<<2] = { '\0' };
int const delayClr = Settings2.WrapAroundTooltipTimeout;
if (delayClr >= (_MQ_TIMER_CYCLE << 3)) {
WCHAR wchToolTip[80] = { L'\0' };
GetLngString(idsMsg, wchToolTip, COUNTOF(wchToolTip));
WideCharToMultiByte(Encoding_SciCP, 0, wchToolTip, -1, chToolTip, (int)COUNTOF(chToolTip), NULL, NULL);
SciCall_CallTipShow(pos, chToolTip);
_DelayClearCallTip(delayClr);
} else {
Sci_CallTipCancelEx();
}
}
//=============================================================================
//
// PasteBoardTimerProc()

View File

@ -98,6 +98,7 @@ void ParseCommandLine();
bool CheckAutoLoadMostRecent();
void ShowZoomCallTip();
void ShowWrapAroundCallTip(bool forwardSearch);
void ShowBraceMatchCallTip(DocPos pos, UINT idsMsg);
void NP3_ZoomIn();
void NP3_ZoomOut();

View File

@ -206,7 +206,7 @@
### Navigation
- [ ] **(Q3) Navigate Backward/Forward** - VS Code-like history navigation
- [ ] **(Q2) Go to Block Start/End** - Jump to enclosing block
- [ ] **(Q2) Brace Find Enhancement** - Search backward for nearest brace when not at one
- [x] **(Q2) Brace Find Enhancement** - Search backward for nearest brace when not at one - ✅ IMPLEMENTED
- Issue: [#4863](https://github.com/rizonesoft/Notepad3/issues/4863)
- [ ] **(Q2) Go to Sibling Block** - Navigate between sibling code blocks
- [ ] **(Q2) Touchpad Horizontal Scroll** - Direct touchpad left/right scrolling