diff --git a/src/Config/Config.cpp b/src/Config/Config.cpp index ed6d41428..82098e094 100644 --- a/src/Config/Config.cpp +++ b/src/Config/Config.cpp @@ -2616,17 +2616,24 @@ bool MRU_Add(LPMRULIST pmru, LPCWSTR pszNew, cpi_enc_t iEnc, DocPos iPos, DocPos int i = 0; for (; i < pmru->iSize; ++i) { if (_MRU_Compare(pmru, pmru->pszItems[i], pszNew) == 0) { - LocalFree(pmru->pszItems[i]); // StrDup() - pmru->pszItems[i] = NULL; break; } } i = min_i(i, pmru->iSize - 1); + // Detach the outgoing entry at slot i (deferred free — pszBookMarks parameter + // may alias pmru->pszBookMarks[i], so we must not free until after StrDup). + LPWSTR pszOldItem = pmru->pszItems[i]; + pmru->pszItems[i] = NULL; + LPWSTR pszOldBookMarks = pmru->pszBookMarks[i]; + pmru->pszBookMarks[i] = NULL; + for (; i > 0; i--) { pmru->pszItems[i] = pmru->pszItems[i - 1]; pmru->iEncoding[i] = pmru->iEncoding[i - 1]; pmru->iCaretPos[i] = pmru->iCaretPos[i - 1]; pmru->iSelAnchPos[i] = pmru->iSelAnchPos[i - 1]; + pmru->pszBookMarks[i] = pmru->pszBookMarks[i - 1]; + pmru->bDirty[i] = pmru->bDirty[i - 1]; } pmru->pszItems[0] = StrDup(pszNew); // LocalAlloc() @@ -2634,6 +2641,12 @@ bool MRU_Add(LPMRULIST pmru, LPCWSTR pszNew, cpi_enc_t iEnc, DocPos iPos, DocPos pmru->iCaretPos[0] = (Settings.PreserveCaretPos ? iPos : -1); pmru->iSelAnchPos[0] = (Settings.PreserveCaretPos ? iSelAnc : -1); pmru->pszBookMarks[0] = (pszBookMarks ? StrDup(pszBookMarks) : NULL); // LocalAlloc() + pmru->bDirty[0] = true; + + // Now safe to free old pointers (StrDup above made independent copies) + if (pszOldItem) { LocalFree(pszOldItem); } + if (pszOldBookMarks) { LocalFree(pszOldBookMarks); } + return true; } return false; @@ -2677,18 +2690,23 @@ bool MRU_AddPath(LPMRULIST pmru, const HPATHL hpth, bool bRelativePath, bool bUn if (pmru) { int i = 0; bool const bAlreadyInList = MRU_FindPath(pmru, hpth, &i); - if (bAlreadyInList) { - LocalFree(pmru->pszItems[i]); // StrDup() - pmru->pszItems[i] = NULL; - } else { + if (!bAlreadyInList) { i = (i < pmru->iSize) ? i : (pmru->iSize - 1); } + // Detach the outgoing entry at slot i (deferred free — pszBookMarks parameter + // may alias pmru->pszBookMarks[i], so we must not free until after StrDupW). + LPWSTR pszOldItem = pmru->pszItems[i]; + pmru->pszItems[i] = NULL; + LPWSTR pszOldBookMarks = pmru->pszBookMarks[i]; + pmru->pszBookMarks[i] = NULL; + for (; i > 0; i--) { pmru->pszItems[i] = pmru->pszItems[i - 1]; pmru->iEncoding[i] = pmru->iEncoding[i - 1]; pmru->iCaretPos[i] = pmru->iCaretPos[i - 1]; pmru->iSelAnchPos[i] = pmru->iSelAnchPos[i - 1]; pmru->pszBookMarks[i] = pmru->pszBookMarks[i - 1]; + pmru->bDirty[i] = pmru->bDirty[i - 1]; } HPATHL hpth_cpy = Path_Copy(hpth); @@ -2701,9 +2719,14 @@ bool MRU_AddPath(LPMRULIST pmru, const HPATHL hpth, bool bRelativePath, bool bUn pmru->iCaretPos[0] = (Settings.PreserveCaretPos ? iPos : -1); pmru->iSelAnchPos[0] = (Settings.PreserveCaretPos ? iSelAnc : -1); pmru->pszBookMarks[0] = (pszBookMarks ? StrDupW(pszBookMarks) : NULL); // LocalAlloc() + pmru->bDirty[0] = true; Path_Release(hpth_cpy); + // Now safe to free old pointers (StrDupW above made independent copies) + if (pszOldItem) { LocalFree(pszOldItem); } + if (pszOldBookMarks) { LocalFree(pszOldBookMarks); } + return bAlreadyInList; } return false; @@ -2802,6 +2825,7 @@ bool MRU_Empty(LPMRULIST pmru, bool bExceptLeast, bool bDelete) pmru->iEncoding[i] = 0; pmru->iCaretPos[i] = -1; pmru->iSelAnchPos[i] = -1; + pmru->bDirty[i] = false; if (pmru->pszBookMarks[i]) { LocalFree(pmru->pszBookMarks[i]); // StrDup() pmru->pszBookMarks[i] = NULL; @@ -2940,8 +2964,27 @@ bool MRU_MergeSave(LPMRULIST pmru, bool bAddFiles, bool bRelativePath, bool bUne if (pmru->pszItems[i]) { Path_Reset(hpth, pmru->pszItems[i]); Path_AbsoluteFromApp(hpth, true); - MRU_AddPath(pmruBase, hpth, bRelativePath, bUnexpandMyDocs, - pmru->iEncoding[i], pmru->iCaretPos[i], pmru->iSelAnchPos[i], pmru->pszBookMarks[i]); + + int idxInBase = 0; + bool bInBase = MRU_FindPath(pmruBase, hpth, &idxInBase); + + if (!bInBase || pmru->bDirty[i]) { + // New entry not in INI, or entry whose metadata was updated + // during this session (visited file): use in-memory props. + MRU_AddPath(pmruBase, hpth, bRelativePath, bUnexpandMyDocs, + pmru->iEncoding[i], pmru->iCaretPos[i], + pmru->iSelAnchPos[i], pmru->pszBookMarks[i]); + } else { + // File is in INI and was NOT visited this session: + // its INI props may have been updated by another instance. + // Preserve the INI values so only ordering is updated. + cpi_enc_t const iBaseEnc = pmruBase->iEncoding[idxInBase]; + DocPos const iBasePos = pmruBase->iCaretPos[idxInBase]; + DocPos const iBaseAnc = pmruBase->iSelAnchPos[idxInBase]; + LPCWSTR const pBaseMarks = pmruBase->pszBookMarks[idxInBase]; + MRU_AddPath(pmruBase, hpth, bRelativePath, bUnexpandMyDocs, + iBaseEnc, iBasePos, iBaseAnc, pBaseMarks); + } } } Path_Release(hpth); @@ -2955,6 +2998,7 @@ bool MRU_MergeSave(LPMRULIST pmru, bool bAddFiles, bool bRelativePath, bool bUne } MRU_Save(pmruBase); + MRU_Destroy(pmruBase); pmruBase = NULL; CloseSettingsFile(__func__, true); diff --git a/src/Notepad3.c b/src/Notepad3.c index 635f8cf05..df170437f 100644 --- a/src/Notepad3.c +++ b/src/Notepad3.c @@ -11303,6 +11303,7 @@ bool FileLoad(const HPATHL hfile_pth, const FileLoadFlags fLoadFlags, const DocP } if (!(Flags.bDoRelaunchElevated || s_IsThisAnElevatedRelaunch)) { MRU_AddPath(Globals.pFileMRU, Paths.CurrentFile, Flags.RelativeFileMRU, Flags.PortableMyDocs, fioStatus.iEncoding, iCaretPos, iAnchorPos, pszBookMarks); + pszBookMarks = Globals.pFileMRU->pszBookMarks[0]; // MRU_AddPath freed the old pointer; slot 0 has the fresh copy AddFilePathToRecentDocs(Paths.CurrentFile); } @@ -11593,6 +11594,7 @@ static void _MRU_UpdateSession() Globals.pFileMRU->iEncoding[idx] = Encoding_GetCurrent(); Globals.pFileMRU->iCaretPos[idx] = bSkipCaretMRU ? -1 : SciCall_GetCurrentPos(); Globals.pFileMRU->iSelAnchPos[idx] = bSkipCaretMRU ? -1 : (Sci_IsMultiOrRectangleSelection() ? -1 : SciCall_GetAnchor()); + Globals.pFileMRU->bDirty[idx] = true; WCHAR wchBookMarks[MRU_BMRK_SIZE] = { L'\0' }; EditGetBookmarkList(Globals.hwndEdit, wchBookMarks, COUNTOF(wchBookMarks)); if (Globals.pFileMRU->pszBookMarks[idx]) { diff --git a/src/TypeDefs.h b/src/TypeDefs.h index e89e0448d..7f3496175 100644 --- a/src/TypeDefs.h +++ b/src/TypeDefs.h @@ -340,6 +340,7 @@ typedef struct MRULIST { DocPos iCaretPos[MRU_MAXITEMS]; DocPos iSelAnchPos[MRU_MAXITEMS]; LPWSTR pszBookMarks[MRU_MAXITEMS]; + bool bDirty[MRU_MAXITEMS]; } MRULIST, *PMRULIST, *LPMRULIST; diff --git a/todo/TODO.md b/todo/TODO.md index 745463fd9..de3e4311f 100644 --- a/todo/TODO.md +++ b/todo/TODO.md @@ -1,8 +1,8 @@ # Notepad3 TODO -## Ideas (deliberated) -- [ ] PCRE2 backward search seems to be slow - ask Claude for analysis +## Ideas / New Features (deliberated) - [ ] Merge/Cleanup all old documentation (Build/Docs/*.txt, etc.) files +- [ ] PCRE2 backward search seems to be slow - ask Claude for analysis ## High Priority @@ -40,9 +40,6 @@ - [x] **(Q2) BUG: Initial window position not working** - Position settings ignored - [x] **To be analyzed - works as designed ???** - ⚠ Validation ❗ - Issue: [#4725](https://github.com/rizonesoft/Notepad3/issues/4725) -- [x] **(Q3) BUG: Regex replace issue** - Verify if still present - ✅ FIXED -- [x] Issue: [#3531](https://github.com/rizonesoft/Notepad3/issues/3531) - ✅ FIXED - - [x] ✅ FIXED - Was no Bug but bad RegEx pattern design (expectation vs. what regex really does) - [ ] **(Q2) BUG: Minipath options don't save** - FullRowSelect/TrackSelect broken - Issue: [#4116](https://github.com/rizonesoft/Notepad3/issues/4116) - [x] **(Q1) BUG: Monitoring log not saved** - ✅ FIXED @@ -56,16 +53,13 @@ - Issue: [#5151](https://github.com/rizonesoft/Notepad3/issues/5151) - [ ] **(Q3) BUG: grepWinNP3 crash** - Right-click search results crashes - Issue: [#5158](https://github.com/rizonesoft/Notepad3/issues/5158) -- [ ] **(Q2) BUG: PHP comment toggle** - Ctrl+Q not working in Web Source Code +- [x] **(Q2) BUG: PHP comment toggle** - Ctrl+Q not working in Web Source Code - ✅ FIXED - Issue: [#5163](https://github.com/rizonesoft/Notepad3/issues/5163) - [ ] **(Q2) BUG: AltGr shortcut conflict** - Can't type `}` `@` on non-US keyboards - Issue: [#5220](https://github.com/rizonesoft/Notepad3/issues/5220) - [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 -- [ ] **(Q2) BUG: Highlight current line broken** - Settings not respected (regression) - - Issue: [#5270](https://github.com/rizonesoft/Notepad3/issues/5270) - - **This is a discussion, about limited line highlite rule language in schema definition ** - [x] **(Q2) BUG: File lock held too long on save** - Blocks FileSystemWatcher - ✅ FIXED - [ ] **Needs validation** - Issue: [#5301](https://github.com/rizonesoft/Notepad3/issues/5301) @@ -180,7 +174,14 @@ - [ ] **(Q3) Custom Keyboard Shortcuts** - User-configurable shortcut keys - Issue: [#595](https://github.com/rizonesoft/Notepad3/issues/595) -## Feature Ideas +## Open Discussions + +- [ ] **(Q2) BUG: Highlight current line broken** - Settings not respected (regression) + - Issue: [#5270](https://github.com/rizonesoft/Notepad3/issues/5270) + - **This is a discussion, about limited line highlite rule language in schema definition ** +- [x] **(Q3) BUG: Regex replace issue** - Verify if still present - ✅ FIXED + - Issue: [#3531](https://github.com/rizonesoft/Notepad3/issues/3531) + - Was no Bug but bad RegEx pattern design (expectation vs. what regex really does) ### Text Processing - [ ] **(Q1) Strip Leading Blanks** - Trim leading whitespace (NP3 only has trailing)