martanne / vis

A vi-like editor based on Plan 9's structural regular expressions
Other
4.28k stars 264 forks source link

Make M go to middle line of window #1136

Open lobre opened 1 year ago

lobre commented 1 year ago

Hello,

I know that M does Restore selections from mark. But it replaces in my opinion an important motion of vim which is to go to the middle line of window.

                        *M*
M           To Middle line of window, on the first non-blank
            character |linewise|.  See also 'startofline' option.

I find it weird that vis has L, H but not M. It is not really consistent in my opinion.

Also, in the docs (:help) in the OPERATOR-PENDING section, it is still written:

  M                     Move cursor to middle line of the window

So it seems the doc has not been updated in this section. Or otherwise, it is that I don't understand what M means in the context of "operator-pending".

I don't have a better replacement for restore selections from mark at the moment. And I understand that it resonates with m being attributed to marks globally in vim. But I still it is worth exploring if something better could be found.

rnpnr commented 1 year ago

That looks like an ancient bug. I believe it should be gM which is inline with :

  gm                    Move cursor to middle of screen/display line

Does that make more sense?

lobre commented 1 year ago

For me, it seems gM means yet another thing in vim. gm and gM are for horizontal moves. The former goes to the column considered as the center of the screen, while the latter is for the center of the line.

gm

Like g0 but half a screenwidth to the right. 

gM

Like g0 but to halfway the text of the line.

I don't know if gM is currently implemented this way in vis, but if that is the case, it means it cannot be used for moving the cursor vertically to the middle line.

rnpnr commented 1 year ago

Based on that it seems that gm in vis is similar to what gM in vim does. The behaviour of gm in vim makes no sense for vis; we do not and will not support horizontal scrolling viewports so going half a screen over is useless.

To me gM - [g]oto [M]iddle of view makes the most sense to me given what gm does. I think it should be fixed that way and a section can potentially be added to the "Differences from Vi(m)" wiki page.

lobre commented 1 year ago

I did not realize that gM was not implemented and that horizontal scrolling did not exist in vis 🥳.

So gM is actually free.

Do you think it makes more sense to have gM doing goto Middle of view vertically and M doing Restore selections from mark or the opposite?

lobre commented 1 year ago

To me having L, M and H consistent for moving around makes sense.

Also not exactly related, but I feel weird that we have g_ for Move cursor to last non-blank character of the line, but we are missing _ which in vim moves the cursor to the first non-blank character.

In vis, it is Remove leading and trailing white space from selections.

I know vis does not have to be fully compliant with vim, but for "matching/related" motions such as those quoted here, I find the behaviour in vis rather inconsistent not only with vim, but also by nature.

rnpnr commented 1 year ago

I can see the argument for using M that way since that is what M does in traditional vi and vim. However I'm not sure internal consistency of vis' more powerful features should be dropped in favour of consistency with vi/vim. I would be interested in hearing what other people think.

we have g_ but we are missing _

It seems like g_ was probably added for compatibility. We have ^ to go to the first non blank. I would be against changing _ since that has been around for most of the life of vis and I use it a lot. I'm sure its the same with other long term vis users.

lobre commented 1 year ago

I am also interested in hearing more about other people's opinions on the topic. I personally think that the basic editing features should be as consistent as possible with vim, and that vis should add up those extra features around multi-cursors and sam expressions.

This is an ideal statement of course, and in practice, there are use cases with clashes where decisions should be taken. What makes vim popular is the style of editing. So for me, this has to be strongly applied in vis so that people are directly familiar. And then, they can learn about those superpowers that vis brings, while staying a minimalist and well-scoped editor.

Otherwise, I just want to mention that in vim, ^ and _ are not exactly identical, and the same for $ and g_.

Let me start with the latter.

Both $ and g_ go To the last non-blank character of the line and [count - 1] lines downward |inclusive|.

However, the small difference is for visual mode. No changes for g_, but for $, In Visual mode the cursor goes to just after the last character in the line.. So the end-of-line character is caught.

So if you want to visually select until the end of the line without catching the end-of-line, you should use g_.

Now for ^ and _, the main difference is that _ takes a count. So if I do 2_, I will be on the first non-blank character of the line just below (1_ is the same as _). And so this is for example useful at the end of a macro if you want to execute it multiple times and apply it on successive lines.

All those differences seem subtle but for an old accustomed vim user, they are burnt in memory and if they are not the same in vis, they will be painful to revert mentally. Vim is an established language and changing the language is a risky bet in my opinion. And this is what differentiates vis with kakoune for example (which decided to create its own brand new language).

I also have comments about how text-object motions are executed in vis when the cursor is outside matching objects. But the topic is a bit different so I think I will create another issue to keep them focused.

lobre commented 1 year ago

I would be against changing _ since that has been around for most of the life of vis and I use it a lot.

I would also rebound on this. While I can totally understand your statement, maybe it is important to bring some history and design choices of vim about _.

Disclaimer, I feel like vis took inspiration from Kakoune for _, as in this editor, it also unselect whitespace surrounding each selection. I love using _ in Kakoune and feel that it simplifies a lot the intent. Selections, motions and text objects can be implemented in a purely "dumb way". They do what the user expects. Doing alt+i ( will select everything in the parenthesis, no matter if there are newlines, whitespace or anything. Same, doing x will select the full line, from column 0 to the last columns including the new line. Then, it is up to the user to use _ to strip the selection to the bare minimum. Combining those is like playing a game. I can easily manipulate selections.

If I want to select all lines within those brackets:

my_var = [
  "first",
  "second",
  "last"
];

I can first select inside the brackets with alt+i [, then I strip to the pure content with _, and I choose to go back to line mode with x. It composes really well.

Vis inherits more from vim's design. And in vim, _ is a way to "refer to the current line".

See this comment: https://github.com/neovim/neovim/blob/af7d317f3ff31d5ac5d8724b5057a422e1451b54/src/nvim/normal.c#L5865

/// "_" is a strange motion command that helps make operators more logical.
/// It is actually implemented, but not documented in the real Vi.  This motion
/// command actually refers to "the current line".  Commands like "dd" and "yy"
/// are really an alternate form of "d_" and "y_".  It does accept a count, so
/// "d3_" works to delete 3 lines.

See also this thread which explains why _ is the initial bit that led to "linewise" motions: https://vi.stackexchange.com/a/19746.

Many people say that d3_ makes more sense compared to 3dd when knowing that _ refers to the "current line". It composes better.

So I think vis should either decide to follow vim with this model and implement all the "linewise" behaviors, or drop it and make it clear why in the docs (but this is dropping well-established vim workflow). And of course, if those do not exist, we should make sure that what they do can be replaced by something else in vis. In this occasion, g_ alone also loses its meaning, so if you don't want _, you'd better drop g_ as well.

ninewise commented 1 year ago

About using M for going to the middle of the window. I don't consider this a particularly important motion so 2 characters for this seems OK to me. gM would be consistent with gm. I certainly think "restore selections" is a more important feature of vis so it seems right to prioritize it's keybinding. It's true that this is inconsistent with L and H, but I'd rather change those.

About g_. As rnpnr said, we used to just have ^ and $ so we were free to use _ for stripping. Not sure why we'd have g_. I'd prefer to have something for 'go to previous non-blank character' as a more basic motion, and leave people free to bind g_ to that if they want, or use $<previous-non-blank> with whatever we pick for it. Especially if you also have a next-non-blank, so ^ could be a combination of basic motions, too.

Over all, I think we have quite different feelings about what 'basic editing features' are. For me, the modes, hjkl together with webfdr and the counts about cover 'basic editing features'.

vis does not aim at vim compatibility, and personally I think it's OK if deep vim powerusers aren't comfortable using it. Rather I consider it a refreshing and light alternative which was inspired by the basic ideas of vi and other nice editors.

lobre commented 1 year ago

I'd prefer to have something for 'go to previous non-blank character' as a more basic motion

I am not sure to follow you here. Isn't that B?

Otherwise, vis is inspired by vim. It does not have to follow it precisely, but to me it is a really hard task to draw the line about what to include and what not. If you include too few, people will always ask for the addition of xxx feature which is missing. And you will never have a concrete reason about not including except: this is my personal feeling as a maintainer (or user of vis). And of course you are biased regarding which vim features you were using frequently vs which you did not use.

All the vim emulation modes out there in editors or in vim clones are living the same story. They are lacking feature either because they decided so, or because they have technical reasons that prevent them from implementing the feature correctly. And this makes a poor vim experience and people end up retiring from using the tool.

I think that choices should be driven by concrete reason, and not about feelings. Of course we can disagree here, and including just whatever you feel like including is a strategy. But regarding the future longevity of this piece of software, I think it is precarious.

Vim is clearly lacking a precise specification about its editing mode (not talking about other features of vim outside of edition such as arglist, quickfix, tabs, mappings, api, vimscript or other bits). But to me, it feels to be a better strategy to stay as close as possible to vim, and exclude features if there are clashing, or judged to be mistakes that we want to correct. But in either cases, this should be solidly argued and documented, so that it does not emanate from feelings.

Neovim did that, but also did a lot more and it's codebase has grown up a lot, making it less and less lightweight and focused. Vis in comparison has got good grounds and a solid design on paper.

ninewise commented 1 year ago

Loric Brevet @.***> wrote:

I'd prefer to have something for 'go to previous non-blank character' as a more basic motion I am not sure to follow you here. Isn't that B?

No, B would go to the character before the previous blank character. E.g. in the string a Bird _ a cursor on the _ would go to B on B but to the d on the motion I suggested.

I agree that "I don't feel like adding this" is a bad reply. For me, I prefer not to add things to vis that can already be achieved by combining existing feature. This makes "go to the middle line" something I would consider. A 3_ seems less interesting because that's 2j^. Perhaps we should work on a configuration on the wiki which would add (most) vim bindings we're missing via aliases and some lua functions?

rnpnr commented 1 year ago

Perhaps we should work on a configuration on the wiki which would add (most) vim bindings we're missing via aliases and some lua functions?

Sounds like a good solution to me with the caveat that any mapping that changes a core vis binding needs to be clearly indicated.

I think I should apply the 2 line patch making gM go to the middle line of the window. Its a core motion but the default binding is very hard to use. Without mapping it to another binding I'm only able to get it working by manually setting the mode to OPERATOR_PENDING via lua and then pressing M.

lobre commented 1 year ago

Without mapping it to another binding I'm only able to get it working by manually setting the mode to OPERATOR_PENDING via lua and then pressing M.

This is also what I saw in the doc, but I was not sure how I was supposed to go into OPERATOR_PENDING. And I am not sure why this is implemented this way. Like "why is M mapped to go to the middle line in OPERATOR_PENDING?".

rnpnr commented 1 year ago

It a side effect of how the keybindings are defined. See here: https://github.com/martanne/vis/blob/aa18162e2d62d1a4a618ee65289ba8b1cdeaf29a/config.def.h#L315-L316 It just happens that M is already assigned as something else in all modes besides OPERATOR_PENDING

lobre commented 1 year ago

Makes sense.

If we choose gM for the middle line, should we change L and H to gL and gH to stay consistent?

lobre commented 1 year ago

I am reading vis' documentation again and maybe there is something that I don't understand, but here is the part of the doc that explains marks:

m sets a mark, M restores it. For example, 'am sets the mark a while 'aM restores it.

It seems it works the opposite way of vim. In vim, you set a mark at a with ma. If I understand correctly, in vis you set a mark at a with 'am instead.

(by the way, if you have an explanation of why it is inverted, I am curious ^^).

So those m and M should be prefixed by the name of the mark anyway with 'a.

Hence, can we say that M is restore mark when there is a "pending" mark typed before? And if that is not the case, M is just go to the middle line?

rnpnr commented 1 year ago

It seems it works the opposite way of vim. In vim, you set a mark at a with ma. If I understand correctly, in vis you set a mark at a with 'am instead.

(by the way, if you have an explanation of why it is inverted, I am curious ^^).

Yes that is by design. Marks are kind of like addresses for interactive modes. Once set they can also be used as addresses in commands (but that might have some limitations).

Hence, can we say that M is restore mark when there is a "pending" mark typed before? And if that is not the case, M is just go to the middle line?

No because there is an implicit default mark ('') when none have been specified. For me this would be the most common use (you set a mark with m, then go read/edit some other part of the file, then jump back with M).

lobre commented 1 year ago

No because there is an implicit default mark ('') when none have been specified. For me this would be the most common use (you set a mark with m, then go read/edit some other part of the file, then jump back with M).

Understood.

mcepl commented 1 year ago

So, what remains here as an actionable issue? Or should this be closed?

lobre commented 1 year ago

Nothing has been done or acted upon since the beginning of this issue. So in that regard, I don't see how it could be closed. Possible changes that could be implemented here are actionable the the sense of being small and relatively easy. But we don't have a clear consensus and questions are yet to be answered.

Here is a summary.

  1. proposition of using M to go to the middle line of the window

It has been commented that M to mean "restore marks" is considered as being more important. gM has been proposed instead to go the middle line, but no patch was applied.

I then asked this question:

If we choose gM for the middle line, should we change L and H to gL and gH to stay consistent?

I am not particularly content about doing this, but still, the question was not answered.

I understand that vis does not aim for vim compatibility and I respect this, but not having M does mean it also breaks the POSIX spec. All the vi/vim clones I know about have H, L and M defined as in the POSIX spec.

But again, I was just trying to make a proposition for consistency and because it "makes sense to me". But I am no maintainer here and I respect the choice of maintainers if they want to go in a separate direction.

  1. _ does not have the POSIX/vim behavior and is not consistent with g_

Kind of the same thing, nothing has been decided. And it is pretty much the same story. This is basic POSIX vi behavior that is not present in vis. _ is for linewise motions. g_ is here, but no _. Many people rely on _ and not on ^ to go to the beginning of the line as this character is sometimes better located on non-English keyboards.

To remove the inconsistency, there was this proposition.

Not sure why we'd have g_. I'd prefer to have something for 'go to previous non-blank character' as a more basic motion, and leave people free to bind g_ to that if they want or use $<previous-non-blank> with whatever we pick for it. Especially if you also have a next-non-blank, so ^ could be a combination of basic motions, too.

I don't know what should be the last decision on those two ideas. I leave people running the project decide about the outcome. And according to what has been decided and after having clearly explained why, I am not against closing this.