Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vimode: Fix cursor hang with folded lines #1326

Closed
wants to merge 4 commits into from

Conversation

scresto09
Copy link
Contributor

Fix cursor hang when we want to move cursor on top line and this line is folded

To reproduce the problem, when vimode plugin is enabled:
Fold a few lines by clicking the "minus" icon.
Move the cursor to the bottom of these lines and try to move back to the top.
The cursor doesn't go back and seem to hang.

@scresto09 scresto09 closed this Apr 15, 2024
@scresto09 scresto09 reopened this Apr 16, 2024
@scresto09 scresto09 changed the title Fix cursor hang with folded lines Vimode: Fix cursor hang with folded lines Apr 16, 2024
@techee
Copy link
Member

techee commented Apr 25, 2024

I'm not sure this is the correct way to address the issue. I think we should take the folds into account and perform the motion commands on top of the visible lines instead of the document lines. I tried to replace this PR with

#1338

Please let me know what you think about the changes.

@scresto09
Copy link
Contributor Author

In response to pull request #1338

After read the https://sourceforge.net/p/scintilla/bugs/2438/ report, I think the best way to automatically set the cursor position on the visible line is surely to track the SCN_MARGINCLICK event. I did some modification to handle this with the commit 5608b74 .

@techee
Copy link
Member

techee commented May 8, 2024

After read the https://sourceforge.net/p/scintilla/bugs/2438/ report, I think the best way to automatically set the cursor position on the visible line is surely to track the SCN_MARGINCLICK event. I did some modification to handle this with the commit 5608b74 .

Thanks for noticing the problem with line wrapping. I think your general approach is right - I've just slightly rewritten the doc_line_from_visible_delta() function to be easier to grasp by my poor brain, please check fa7025b if it looks good to you.

There are a few things in your patch which I don't think are completely correct though - I'll add some inline comments to your code.

static gint doc_line_from_visible_delta(CmdParams *p, gint line, gint delta, gint *previous)
{
gint step = delta < 0 ? -1 : 1;
gint new_line = p->line;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be line instead of p->line - we don't always pass p->line as the parameter of this function.

pos = SSM(p->sci, SCI_GETLINEENDPOSITION, one_above, 0);

if (previous > -1) {
guint pos = SSM(p->sci, SCI_POSITIONFROMLINE, previous, 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you didn't understand the purpose of this strange "one above" and then SCI_LINEDOWN dance. While you get to the correct line alright, you lose the cursor's x position on the line this way. Notice how, when moving cursor up and down, it remembers the maximum x coordinate on the line and even after bing on lines where the maximum x is lower *like e.g. an empty line where x is 0), it returns back to the right position on longer lines.

Unfortunately this "maximum x" is not possible to obtain from Scintilla but Scintilla automatically recovers it when doing SCI_LINEDOWN or SCI_LINEUP. So the trick here is to first go to the line above or below, and then perform SCI_LINEDOWN or SCI_LINEUP to get us to the right line and get the x position of the cursor. When you remove this code, you'll always end with the x position at the beginning of the line.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think I understood the problem you explained correctly.

But from what I understood, for cmd_goto_up this method is only really necessary when the line you want to go to is not the previous visible line.

With this fix my idea was to have "doc_line_from_visible_delta" fill the "previous" variable with the line number just below the visible line to access it with a SET_POS_NOX, then in any case do a SCI_LINEUP to go to the desired line.

Same for goto_down and SCI_LINEDOWN.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, so it was me who misunderstood your code :-).

Even though your version works, I'll probably go for #1338 which is easier for me to understand - unless you have some problems with it.

SET_POS_NOX(p->sci, pos, FALSE);
}

if (new_line > p->line) SSM(p->sci, SCI_LINEDOWN, 0, 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is all this code necessary? Maybe I'm missing something but I did just this:

fa7025b#diff-15a3a15a958bc6f85e0f1fae419e23c60bbee47bf617ef133f7539fc1f29b277R128

doc_line_from_visible_delta() only returns a line from the valid line range and when you are on the first line, this is already the "line above" for which you then perform SCI_LINEDOWN so it should be OK on this side. On the other end of the document when you are on the last line, SCI_LINEDOWN just won't do anything so there's no need for some special handling there.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just a detail but this code is useful to have the same behavior as VIM.
With VIM, when the cursor is on the last line but not the last character and you press the down arrow, the cursor does not move.
Without this code, the cursor will move to the end of the line.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, yeah, the previous code handled right that, I just forgot why exactly it was there. I've fixed it in my PR.

Copy link
Member

@techee techee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good except for the minor comments below.

I was actually thinking about not doing this at all because it doesn't cover all fold/unfold situations like e.g. using Geany's keybindings for folding but the patch is simple and better than nothing so let's do it.

Would you post this as a separate pull request? If #1338 looks good to you, I'd merge that PR after which this one could be merged.

gint fold_parent = line;

/* go through the parents as long as they are not visible */
while (SSM(sci, SCI_GETLINEVISIBLE, fold_parent, 0) == FALSE) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better !SSM(...) instead of the FALSE comparison.

Also place { on separate line to match the other code style.

while (SSM(sci, SCI_GETLINEVISIBLE, fold_parent, 0) == FALSE) {
gint prev_parent = SSM(sci, SCI_GETFOLDPARENT, fold_parent, 0);

if (prev_parent == -1) break;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

break on separate line.

{
/* move the cursor on the visible line before the fold */
gint pos = SSM(sci, SCI_POSITIONFROMLINE, fold_parent, 0);
SET_POS(sci, pos, TRUE);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use SET_POS_NOX() instead. This one will preserve the "maximum x" cursor value so when further moving up or down, this one will be recovered.

@@ -32,5 +32,8 @@ void perform_substitute(ScintillaObject *sci, const gchar *cmd, gint from, gint
const gchar *flag_override);

gint get_line_number_rel(ScintillaObject *sci, gint shift);
void ensure_current_line_expanded(ScintillaObject *sci);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, remove, it's from another patch.

@@ -306,6 +306,14 @@ gboolean vi_notify_sci(SCNotification *nt)
}
}

if (nt->nmhdr.code == SCN_MARGINCLICK) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{ on separate line.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've just merged #1338 which functionality-wise covers most of this PR but doesn't contain the SCN_MARGINCLICK part. Would you create a separate pull request containing it?

@techee
Copy link
Member

techee commented May 21, 2024

Closing this PR as #1338 was merged. Having SCN_MARGINCLICK handled would however be worth adding as a separate PR.

@techee techee closed this May 21, 2024
@scresto09
Copy link
Contributor Author

OK, I just create #1349 for SCN_MARGINCLICK part.
Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants