diff -r -u src/Editor.cxx src/Editor.cxx
--- a/src/Editor.cxx 2020-07-27 20:00:24.000000000 +0300
+++ b/src/Editor.cxx 2020-12-02 01:11:49.662556755 +0300
@@ -135,7 +135,15 @@
hotSpotClickPos = INVALID_POSITION;
selectionUnit = TextUnit::character;
- lastXChosen = 0;
+ /*
+ See .
+ Seems this is not fully correct for Cocoa, but it works good for Linux+GTK.
+ */
+ lastXChosen = vs.leftMarginWidth;
+ for ( auto const & m : vs.ms ) {
+ lastXChosen += m.width;
+ }
+
lineAnchorPos = 0;
originalAnchorPos = 0;
wordSelectAnchorStartPos = 0;
@@ -3147,13 +3155,36 @@
Point::FromInts(lastX - xOffset, static_cast(newY)), false, false, UserVirtualSpace());
if (direction < 0) {
- // Line wrapping may lead to a location on the same line, so
- // seek back if that is the case.
- Point ptNew = LocationFromPosition(posNew.Position());
- while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
- posNew.Add(-1);
- posNew.SetVirtualSpace(0);
- ptNew = LocationFromPosition(posNew.Position());
+ if (UserVirtualSpace()) {
+ if (posNew > spStart) {
+ /*
+ Looks like SPositionFromLocation works incorrectly in the case when (1) line
+ wrapping is on, (2) the current document line is wrapped, (3) the caret is just
+ moved up, (3) the current display line is shorter than the previous, (4)
+ previous caret column was bigger than current display line length. In such a
+ case posNew is bigger than spStart. Do not move the caret in such a case.
+ */
+ posNew = spStart;
+ } else if (posNew.Position() > 0 && posNew.Position() == StartEndDisplayLine(spStart.Position(), true)) {
+ /*
+ If line wrapping is on, the current display line may be shorter than the
+ previous. If the previous caret column was bigger than current display line
+ length, SPositionFromLocation returns position of the end of current display
+ line with appropriate amount of virtual spaces. Looks ok, but position of the
+ end of current display line also is the position of the beginning of the next
+ display line, and scintilla prefers the second interpretation of the position.
+ */
+ posNew.SetPosition(posNew.Position() - 1);
+ }
+ } else {
+ // Line wrapping may lead to a location on the same line, so
+ // seek back if that is the case.
+ Point ptNew = LocationFromPosition(posNew.Position());
+ while ((posNew.Position() > 0) && (pt.y == ptNew.y)) {
+ posNew.Add(-1);
+ posNew.SetVirtualSpace(0);
+ ptNew = LocationFromPosition(posNew.Position());
+ }
}
} else if (direction > 0 && posNew.Position() != pdoc->Length()) {
// There is an equivalent case when moving down which skips
@@ -3218,14 +3249,24 @@
void Editor::ParaUpOrDown(int direction, Selection::selTypes selt) {
Sci::Line lineDoc;
- const Sci::Position savedPos = sel.MainCaret();
+ auto savedPos = sel.Range(sel.Main()).caret;
+ auto x = UserVirtualSpace() ? XFromPosition(savedPos) : -1;
do {
- MovePositionTo(SelectionPosition(direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret())), selt);
- lineDoc = pdoc->SciLineFromPosition(sel.MainCaret());
+ auto newPos = direction > 0 ? pdoc->ParaDown(sel.MainCaret()) : pdoc->ParaUp(sel.MainCaret());
+ lineDoc = pdoc->SciLineFromPosition(newPos);
+ if (x >= 0) {
+ MovePositionTo(SPositionFromLineX(lineDoc, x), selt);
+ } else {
+ MovePositionTo(newPos, selt);
+ }
if (direction > 0) {
if (sel.MainCaret() >= pdoc->Length() && !pcs->GetVisible(lineDoc)) {
if (selt == Selection::noSel) {
- MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos)));
+ if (x >= 0) {
+ MovePositionTo(savedPos);
+ } else {
+ MovePositionTo(SelectionPosition(pdoc->LineEndPosition(savedPos.Position())));
+ }
}
break;
}
@@ -3771,22 +3812,62 @@
case SCI_LINEENDWRAPEXTEND:
return HorizontalMove(iMessage);
- case SCI_DOCUMENTSTART:
- MovePositionTo(0);
+ case SCI_DOCUMENTSTART: {
+ SelectionPosition newPos;
+ if (UserVirtualSpace()) {
+ newPos = SPositionFromLineX(0, XFromPosition(sel.Range(sel.Main()).caret));
+ } else {
+ newPos.SetPosition(0);
+ }
+ MovePositionTo(newPos);
SetLastXChosen();
break;
- case SCI_DOCUMENTSTARTEXTEND:
- MovePositionTo(0, Selection::selStream);
+ }
+ case SCI_DOCUMENTSTARTEXTEND: {
+ SelectionPosition newPos;
+ if (UserVirtualSpace()) {
+ newPos = SPositionFromLineX(0, XFromPosition(sel.Range(sel.Main()).caret));
+ } else {
+ newPos.SetPosition(0);
+ }
+ MovePositionTo(newPos, Selection::selStream);
SetLastXChosen();
break;
- case SCI_DOCUMENTEND:
- MovePositionTo(pdoc->Length());
+ }
+ case SCI_DOCUMENTEND: {
+ SelectionPosition newPos;
+ if (UserVirtualSpace()) {
+ auto newLine = pdoc->LinesTotal() - 1;
+ /*
+ This is not fully correct. If the last document line is wrapped, the caret will be
+ positioned on the first display line. It seems the last display line would be more
+ suitable.
+ */
+ newPos = SPositionFromLineX(newLine, XFromPosition(sel.Range(sel.Main()).caret));
+ } else {
+ newPos.SetPosition(pdoc->Length());
+ }
+ MovePositionTo(newPos);
SetLastXChosen();
break;
- case SCI_DOCUMENTENDEXTEND:
- MovePositionTo(pdoc->Length(), Selection::selStream);
+ }
+ case SCI_DOCUMENTENDEXTEND: {
+ SelectionPosition newPos;
+ if (UserVirtualSpace()) {
+ Sci::Line newLine = pdoc->LinesTotal() - 1;
+ /*
+ This is not fully correct. If the last document line is wrapped, the caret will be
+ positioned on the first display line. It seems the last display line would be more
+ suitable.
+ */
+ newPos = SPositionFromLineX(newLine, XFromPosition(sel.Range(sel.Main()).caret));
+ } else {
+ newPos.SetPosition(pdoc->Length());
+ }
+ MovePositionTo(newPos, Selection::selStream);
SetLastXChosen();
break;
+ }
case SCI_STUTTEREDPAGEUP:
PageMove(-1, Selection::noSel, true);
break;
@@ -3908,10 +3989,15 @@
}
break;
case SCI_LINEDELETE: {
- const Sci::Line line = pdoc->SciLineFromPosition(sel.MainCaret());
+ const auto caret = sel.Range(sel.Main()).caret;
+ const Sci::Line line = pdoc->LineFromPosition(caret.Position());
+ const auto x = UserVirtualSpace() ? XFromPosition(caret) : -1;
const Sci::Position start = pdoc->LineStart(line);
const Sci::Position end = pdoc->LineStart(line + 1);
pdoc->DeleteChars(start, end - start);
+ if (x >= 0) {
+ MovePositionTo(SPositionFromLineX(line, x));
+ }
}
break;
case SCI_LINETRANSPOSE:
@@ -4182,9 +4268,14 @@
lineNo = pdoc->LinesTotal();
if (lineNo < 0)
lineNo = 0;
- SetEmptySelection(pdoc->LineStart(lineNo));
- ShowCaretAtCurrentPosition();
- EnsureCaretVisible();
+ SelectionPosition newPos;
+ if (UserVirtualSpace()) {
+ newPos = SPositionFromLineX(lineNo, XFromPosition(sel.Range(sel.Main()).caret));
+ } else {
+ newPos.SetPosition(pdoc->LineStart(lineNo));
+ }
+ MovePositionTo(newPos);
+ SetLastXChosen();
}
static bool Close(Point pt1, Point pt2, Point threshold) noexcept {
diff -r -u src/EditView.cxx src/EditView.cxx
--- a/src/EditView.cxx 2020-07-27 20:00:24.000000000 +0300
+++ b/src/EditView.cxx 2020-12-02 01:11:49.663556756 +0300
@@ -655,9 +655,16 @@
const Sci::Line lineDoc = model.pcs->DocFromDisplay(visibleLine);
if (canReturnInvalid && (lineDoc < 0))
return SelectionPosition(INVALID_POSITION);
- if (lineDoc >= model.pdoc->LinesTotal())
- return SelectionPosition(canReturnInvalid ? INVALID_POSITION :
- model.pdoc->Length());
+ if (lineDoc >= model.pdoc->LinesTotal()) {
+ if (canReturnInvalid) {
+ SelectionPosition(INVALID_POSITION);
+ }
+ if (virtualSpace) {
+ return SPositionFromLineX(surface, model, model.pdoc->LinesTotal() - 1, static_cast(pt.x), vs);
+ } else {
+ return SelectionPosition(model.pdoc->Length());
+ }
+ }
const Sci::Position posLineStart = model.pdoc->LineStart(lineDoc);
AutoLineLayout ll(llc, RetrieveLineLayout(lineDoc, model));
if (surface && ll) {