Hi Neil and maintainers,
I think Message::SetUseTabs only toggles wheather or not new tabs will be replaced with spaces, and the layout doesn't change. So, InvalidateStyleRedraw() is not necessary.
This eliminates a bug of Notepad4: doesn't work correctly on rectangular selections. If 0 column is selected, press 1+ times. The Ctrl + Tab feature can date back to the original Notepad2.
Thanks in advance!
useTabsfield is only used insideDocument::SetLineIndentation()andEditor::Indent()methods, call toInvalidateStyleRedraw()can be eliminated.InvalidateStyleRedraw()should be safe to call. If Ctrl+Tab is not working correctly thenMessage::Tabthat it calls may need to perform more setup. If just the change proposed above is done then there may be other ways to reachMessage::Tabin a state that fails.The reason for the failure should be examined more closely and, perhaps fixed inside
Editor::Indentthat is called forMessage::Tab.While the mentioned
InvalidateStyleRedraw()can be removed for performance, the potential failure is more important.The mentioned code block inserts raw tab (
\t) character at caret position:1567before.gif is screenshot with
InvalidateStyleRedraw(),1567after.gif is screenshot without
InvalidateStyleRedraw().Wonder how YX Hao identified the bug and fix.
Apply
1567-Notepad4-debug-1013.diffonto today's Noetpad4 code, build (e.g. click batch file insidebuild\VisualStudio\folder) and run Notepad4.exe from terminal, open a 2 bytes file (\n\n, just two new-lines), holdAltkey to make rectangular selection for the three lines, pressCtrl + Tabtwice.log without
InvalidateStyleRedraw()forMessage::SetUseTabs:log with
InvalidateStyleRedraw():So, the underline bug is not fixed:
SetRectangularRange()may change selection ranges, and causepdoc->DeleteChars()insideIndent()to actually delete characters.Not find a fix, the bug can be reproduced in SciTE with similar code, e.g.:
I think the basic issue here is between a rectangular selection and
Editor::Indentnot behaving in a 'rectangular' way. Each piece of the selection is treated as part of a multiple-selection. The selection type stays rectangular but the range is not updated to match the bounds of all of the selection pieces. So there is a mismatch betweenSelection::rangeRectangularandSelection::ranges.Likely the best approach would be to work out how
Tabshould work in a rectangular context and implement that as a separate branch insideEditor::Indent. Less work would be repairing the relationship between the fields. Possibly by settingSelection::rangeRectangularto match the bounds ofSelection::rangesthen recalculatingSelection::ranges.The role of
InvalidateStyleRedrawis to causeSetRectangularRangeto occur, which does synchronizerangesfromrangeRectangularbut that may be the wrong direction in this case, not matching user preference.without
SetRectangularRange, both caret and anchor positions moved to right, and keeps no text selected.with
SetRectangularRange, caret positions sticked at zero column, anchor positions moved to right, keeps first column selected, which is unexpected.The rectangular range is moved for each tab insertion (which is reasonable) but is then used to recreate the ranges. At the beginning the rectangular range is 0-2, the first line has a tab inserted so the rectangular range is 1-3 which means it is from column 1 on line 0 back to column 0 on line 2, so now contains space (real for line 0, virtual for others).
For rectangular selections,
rangesshouldn't be recalculated fromrangeRectangularfor each line. This could be achieved by copying the initial selection and reverse iterating (to avoid problems from text moving away from the copy). However, that would get in the way of the other cases and complicate the code.One possible technique is to temporarily treat the selection as multiple stream selections then convert back to rectangular at end of
Indent:This may need additional work for other conditions like 'thin' selections.
That works, changes with less code also works:
Just needs
ThinRectangularRange();, no need to changeselType.Last edit: Zufu Liu 2025-10-18
There are more problems when there is text on the lines, deleting text. This is fixed with the above change but not with just calling
ThinRectangularRange.Oh, not tested that.
Sorry, I'm not work on this bug. I just feel both the changes have Implicit assumption about selections and
selTyperetains duringIndent()operation, but origin code does not require the assumption.The scope of this change isn't great but I think it is still positive as it stops some cases where user text was deleted.
I got side tracked when trying to write a test but see different results in a mechanical test than a user test.
I'm not as familiar with the project code as you are. I commented out
SciCall_SetTabIndents()andSciCall_SetUseTabs(), leaving onlySciCall_Tab(). It worked fine (in use Tabs mode). Then narrowed down to the handling ofMessage::SetUseTabsand found thatInvalidateStyleRedraw()was unnecessary. After removing it, it did work, so I didn't look into it.Last edit: YX Hao 2025-10-13
Committed with [403f36], [4200ef], [fb680a].
Changes
Editor::Indentas described above and adds a test. Changes Python test script to make it easier to run inside Visual C++ debugger. RemovesInvalidateStyleRedrawas not needed.Related
Commit: [403f36]
Commit: [4200ef]
Commit: [fb680a]