aligrudi / neatvi

A small vi/ex editor for editing UTF-8 text
http://litcave.rudi.ir/
324 stars 27 forks source link

How to deal without command history? #68

Closed lobre closed 11 months ago

lobre commented 11 months ago

Hi,

As neatvi has neither a command history nor a cedit (like in nvi) to edit command history, how is your workflow to re-enter previous (or slightly modified previous) commands?

I can see it has a special buffer : that contains the previous ex command, and it also has ^p to paste the default buffer in normal mode. So I am able to paste the default buffer in an ex command with ^p, but I cannot paste from other buffers (such as :).

Do you have a workaround to accomplish such a thing?

Thanks

aligrudi commented 11 months ago

Hi,

Loric Brevet @.***> wrote:

As neatvi has neither a command history nor a cedit (like in nvi) to edit command history, how is your workflow to re-enter previous (or slightly modified previous) commands?

I can see it has a special buffer : that contains the previous ex command, and it also has ^p to paste the default buffer in normal mode. So I am able to paste the default buffer in an ex command with ^p, but I cannot paste from other buffers (such as :).

Do you have a workaround to accomplish such a thing?

I have not found a clean solution yet. What I do now, if I need to redo or modify the last command, is to first paste it into a buffer, modify it, and copy it into the default yank buffer.

Ali
lobre commented 11 months ago

Would it make sense to implement ^R<reg> in insert and command mode to insert the content of a register?

It would not help to modify the last command though as it would still require to paste it to a buffer for modification.

aligrudi commented 11 months ago

Loric Brevet @.***> wrote:

Would it make sense to implement ^R<reg> in insert and command mode to insert the content of a register?

Done.

Ali
lobre commented 11 months ago

Thank you for quickly implementing this! I guess that now, using a custom buffer to deal with command history is handy enough 🎉.

Maybe having a mapping that executes the line under the cursor as a command could simplify the process even more. A bit the same as gf for opening a file, but instead it would execute an ex command. That would avoid having to copy/paste into command mode. But this would maybe be going too far.

In the same vein, the plan 9 acme editor provides the developer with two main actions (triggered on mouse buttons 2 and 3 on a 3-button mouse) that either open the file under cursor (or currently selected) or "execute" the current line/word/selected text as a command.

Having something like this could help also to execute shell commands as one could write ex commands starting with an exclamation mark (e.g. :!ls -l).

I see this system as having "good leverage with small footprint" as it would allow having interesting custom workflows just by leveraging buffers and ex commands. Something to dig!

aligrudi commented 11 months ago

Loric Brevet @.***> wrote:

Thank you for quickly implementing this! I guess that now, using a custom buffer to deal with command history is handy enough 🎉.

Maybe having a mapping that executes the line under the cursor as a command could simplify the process even more. A bit the same as gf for opening a file, but instead it would execute an ex command. That would avoid having to copy/paste into command mode. But this would maybe be going too far.

We already have @ normal mode command, which executes the contents of any register. We can introduce a special register for the current line; for instance = so that @= executes the current line. Any suggestions for the register name?

I first tried having a separate # command for executing the contents registers as ex commands; just like @, which is for vi commands. However, that does not seem necessary: if the register (or current line) begins with a colon, @ executes it as an ex command.

Ali
lobre commented 11 months ago

We already have @ normal mode command, which executes the contents of any register. We can introduce a special register for the current line; for instance = so that @= executes the current line.

This is smart! I do like when we avoid adding new features, but instead consolidate and integrate with concepts that are already existing. This is the elegance of a good design, empowering orthogonality and composition without adding too much overhead.

Any suggestions for the register name?

We could stay consistent with vim maybe for this?

    ":  Contains the most recent executed command-line.  Example: Use
        "@:" to repeat the previous command-line command.
        The command-line is only stored in this register when at least
        one character of it was typed.  Thus it remains unchanged if
        the command was completely from a mapping.

In vim, = is the expression register. I am not against using it, but using : makes more sense to me as it represents the command line.

aligrudi commented 11 months ago

Loric Brevet @.***> wrote:

We could stay consistent with vim maybe for this?

  ":  Contains the most recent executed command-line.  Example: Use
      "@:" to repeat the previous command-line command.
      The command-line is only stored in this register when at least
      one character of it was typed.  Thus it remains unchanged if
      the command was completely from a mapping.

In vim, = is the expression register. I am not against using it, but using : makes more sense to me as it represents the command line.

This is for the previous command not the current line.

Ali
lobre commented 11 months ago

Sorry I misread your comment. If searching for a name that would best represent a line, maybe _ could be it (hence @_).

The _ can be referenced as the "linewise" motion.

_  <underscore>     [count] - 1 lines downward, on the first non-blank

More info about the ins and outs of this motion here:

https://vi.stackexchange.com/a/19746

I don't know if this translates fully to our exemple here but I don't find any other "obvious" names.

Maybe there is not such a thing and @= might not be bad after all.

aligrudi commented 11 months ago

Loric Brevet @.***> wrote:

Sorry I misread your comment. If searching for a name that would best represent a line, maybe _ could be it (hence @_).

The _ can be referenced as the "linewise" motion.

_  <underscore>     [count] - 1 lines downward, on the first non-blank

More info about the ins and outs of this motion here:

https://vi.stackexchange.com/a/19746

I don't know if this translates fully to our exemple here but I don't find any other "obvious" names.

Maybe there is not such a thing and @= might not be bad after all.

OK. I have assigned register ; to the current line. Now it is also possible to use %, " and . registers for the current file, the default yank buffer, and the last vi command, respectively.

Ali
lobre commented 11 months ago

This is neat, thank you! I will play with this.

When I build now, I do have warning though (simply using make):

cc -c -Wall -O2 reg.c
reg.c: In function ‘reg_putraw’:
reg.c:38:18: warning: array subscript [-128, 255] is outside array bounds of ‘char *[256]’ [-Warray-bounds]
   38 |         free(bufs[tolower(c)]);
      |              ~~~~^~~~~~~~~~~~
reg.c:7:14: note: while referencing ‘bufs’
    7 | static char *bufs[256];
      |              ^~~~
reg.c:39:13: warning: array subscript [-128, 255] is outside array bounds of ‘char *[256]’ [-Warray-bounds]
   39 |         bufs[tolower(c)] = buf;
      |         ~~~~^~~~~~~~~~~~
reg.c:7:14: note: while referencing ‘bufs’
    7 | static char *bufs[256];
      |              ^~~~
lobre commented 11 months ago

And another question, how to represent the "escape" character in a written command in a register?

For example, I have this line that I could now execute with @;:

cwnewword<esc>jdd

Is there a way to represent this <esc> so that I go back to normal mode?

And same question for the newline character:

cwnewword\nandnewline<esc>

Here in my example \n is literally entered, but I am searching for a way to enter a real newline character.

lobre commented 11 months ago

Sorry for the spam, I am just reporting while exploring ^^.

I see that the : register does not work correctly when there are two splits. It does not contain the last command, but instead, it always contains e! unnamed.

aligrudi commented 11 months ago

Loric Brevet @.***> wrote:

This is neat, thank you! I will play with this.

When I build now, I do have warning though (simply using make):

cc -c -Wall -O2 reg.c
reg.c: In function ‘reg_putraw’:
reg.c:38:18: warning: array subscript [-128, 255] is outside array bounds of ‘char *[256]’ [-Warray-bounds]
   38 |         free(bufs[tolower(c)]);
      |              ~~~~^~~~~~~~~~~~
reg.c:7:14: note: while referencing ‘bufs’
    7 | static char *bufs[256];
      |              ^~~~
reg.c:39:13: warning: array subscript [-128, 255] is outside array bounds of ‘char *[256]’ [-Warray-bounds]
   39 |         bufs[tolower(c)] = buf;
      |         ~~~~^~~~~~~~~~~~
reg.c:7:14: note: while referencing ‘bufs’
    7 | static char *bufs[256];
      |              ^~~~

The compiler seems wrong.

Ali
aligrudi commented 11 months ago

Loric Brevet @.***> wrote:

And another question, how to represent the "escape" character in a written command in a register?

For example, I have this line that I could now execute with @;:

cwnewword<esc>jdd

Is there a way to represent this <esc> so that I go back to normal mode?

You can use ^v^[.

And same question for the newline character:

cwnewword\nandnewline<esc>

Here in my example \n is literally entered, but I am searching for a way to enter a real newline character.

You cannot do that in a single line. You can store the multi-line command in a register and execute that.

Ali
aligrudi commented 11 months ago

Loric Brevet @.***> wrote:

Sorry for the spam, I am just reporting while exploring ^^.

I see that the : register does not work correctly when there are two splits. It does not contain the last command, but instead, it always contains e! unnamed.

That is the command neatvi executes to switch between buffers. Now it stores user commands only.

Ali
lobre commented 11 months ago

The compiler seems wrong.

Here is what I have.

$ which cc
/usr/bin/cc

$ ls -l /usr/bin/cc
lrwxrwxrwx 1 root root 20 Jul 20 15:12 /usr/bin/cc -> /etc/alternatives/cc

$ ls -l /etc/alternatives/cc
lrwxrwxrwx 1 root root 12 Jul 20 15:12 /etc/alternatives/cc -> /usr/bin/gcc

$ which gcc
/usr/bin/gcc

$ gcc --version
gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
lobre commented 11 months ago

I tried in an Alpine container and a fresh build system and it is all good. The issue seems to be on my NixOS box. So no red lights here in the end.

Your recent changes have an impact and enable an efficient editing workflow. Thank you a lot.

I still have a few remarks.

  1. What is the purpose of the . register? I mean it is cool to have access to the last vi command, but do you have an example in mind of where/when it could be useful?
  2. Thinking more about this "current-line register", I am more and more convinced that _ would be a better name as it reflects the "line" motion already present in vi with d_, y_, ... Do you have a counter-argument or a reason to prefer ; or is it arbitrary? On the flip side, I know that vim has the _ as being the "black-hole register", but I am not sure this is a necessary feature in neatvi.
  3. We have the : register containing the previous ex command. But this register does not contain the actual : character. Which means one cannot simply reapply the latest command with @:. That would be nice behavior though. If this is not desired, maybe there should be a special case which says that when applying this register with @, a : should be prepended?

For this 3. nvi does have a command to reapply previous substitution:

&       Repeat the previous substitution command on the current line.

But if we have @:, that would be a superset of this feature, as we would be able to reapply the previous ex command to the current line generally speaking.

aligrudi commented 11 months ago

Loric Brevet @.***> wrote:

But if we have @:, that would be a superset of this feature, as we would be able to reapply the previous ex command to the current line generally speaking.

OK. Now Neatvi prefixes : register with a colon, to make repeating the previous ex command with @: possible.

lobre commented 11 months ago

Indeed. That makes @: really smooth. Though on the flip side, it makes ^R: more cumbersome when doing it in ex mode, because it will add two ::. Not sure what could be done here.

aligrudi commented 11 months ago

Loric Brevet @.***> wrote:

Indeed. That makes @: really smooth. Though on the flip side, it makes ^R: more cumbersome when doing it in ex mode, because it will add two ::. Not sure what could be done here.

As ex ignores excess colons, it probably doesn't matter.

Ali
lobre commented 11 months ago

As ex ignores excess colons, it probably doesn't matter.

This can make sense.

Do you have other comments about command history in general (like things in the workflow that are required to slightly improve)? Or should I close this issue?

aligrudi commented 11 months ago

Loric Brevet @.***> wrote:

As ex ignores excess colons, it probably doesn't matter.

This can make sense.

Do you have other comments about command history in general (like things in the workflow that are required to slightly improve)? Or should I close this issue?

I cannot think of a simple, clean, and convenient improvement at the moment.

Ali
lobre commented 11 months ago

Ok, closing this issue then. Thank you again for all the work on this topic!