FarManager icon indicating copy to clipboard operation
FarManager copied to clipboard

Redundant empty lines after FCTL_SETUSERSCREEN

Open johnd0e opened this issue 7 months ago • 49 comments

Consider such code:

local function print (str)
  panel.GetUserScreen()
  win.WriteConsole(str.."\n")
  panel.SetUserScreen()
end

print(1)
print(2)
print(3)

Output is:

1

2

3

Perhaps it would be better to add extra newline only when Far gets control back. It it possible?

johnd0e avatar May 24 '25 17:05 johnd0e

Even worser case:

panel.GetUserScreen()
print 1
print 2
print 3
panel.SetUserScreen()

Output:

1



2



3


johnd0e avatar May 24 '25 17:05 johnd0e

Presumably broken between 6360 (good) and 6373 (bad)

johnd0e avatar May 24 '25 18:05 johnd0e

Are you sure 6360 was good?

Image

alabuzhev avatar May 24 '25 20:05 alabuzhev

Well, bisect was performed by Shmuel, here is the picture from him:

IMG_20250524_225146_975.jpg

johnd0e avatar May 24 '25 20:05 johnd0e

Update from Shmuel: bad is 6368

drkns 2024-09-10 19:37:57+01:00 - build 6368

  1. Minor visual correction of 6348.

johnd0e avatar May 24 '25 21:05 johnd0e

Thanks, it looks like the right place indeed, but, as already mentioned, I cannot reproduce it.

What I did:

  • Put your code into a file, e.g. 957.lua
  • Downloaded a version < 6368 from the releases page
  • Unpacked it to a new directory
  • Created Far.exe.ini with UseSystemProfiles = 0 there
  • Started it
  • Invoked lua:@<path>\957.lua

If steps to reproduce are different, then please specify them, I cannot read minds.

alabuzhev avatar May 25 '25 08:05 alabuzhev

@alabuzhev , my file for testing was as follows:

mf.printconsole(1)
mf.printconsole(2)
mf.printconsole(3)

@johnd0e gave me several test examples yesterday but I used only one of them (as I was busy with other things).

shmuz avatar May 25 '25 09:05 shmuz

Thanks, that one works.

alabuzhev avatar May 25 '25 09:05 alabuzhev

6482

alabuzhev avatar May 25 '25 14:05 alabuzhev

  1. As I can see now Far does not add empty line after receiving control back. That is acceptable, but still I would prefer to get the empty line automatically
  2. Consider the case from the last post: https://github.com/FarGroup/FarManager/issues/957#issuecomment-2909192357

Or this equivalent

panel.GetUserScreen()
mf.printconsole(1)
mf.printconsole(2)
mf.printconsole(3)
panel.SetUserScreen()

1


2


3

In my opinion the output in both cases should not differ.

johnd0e avatar May 26 '25 10:05 johnd0e

In my opinion the output in both cases should not differ

Was it the same in 6367 (or whatever is considered 'good')?

alabuzhev avatar May 26 '25 10:05 alabuzhev

Ugh, I'm tempted to wash it all away and rewrite.

How it should work, in my opinion:

  • A FCTL_GETUSERSCREEN shows the canvas and adds an empty line after the previous text (if param1 is 0 and if it is actually needed).
  • A FCTL_SETUSERSCREEN adds an empty line after whatever plugin has written (if param1 is 0 and if it is actually needed) and one or two more empty lines to make space for the command line and the keybar (if needed) and takes a snapshot.
  • A nested FCTL_GETUSERSCREEN does not show the canvas (as it is already shown), just optionally adds an empty line if needed.
  • A nested FCTL_SETUSERSCREEN optionally adds an empty line if needed. It knows that it is nested so it doesn't try to make space for for the command line and the keybar.

Examples:

mf.printconsole("1\n")
mf.printconsole("2\n")
mf.printconsole("3\n")

will produce

...whatever...

1

2

3

C:\...
1Help   2UserMn
mf.printconsole("1")
mf.printconsole("2")
mf.printconsole("3")

will produce

...whatever...

1

2

3

C:\...
1Help   2UserMn
panel.GetUserScreen()
mf.printconsole("1")
mf.printconsole("2")
mf.printconsole("3")
panel.SetUserScreen()

will produce

...whatever...

1

2

3

C:\...
1Help   2UserMn
panel.GetUserScreen(-1, 1)
mf.printconsole("1")
mf.printconsole("2")
mf.printconsole("3")
panel.SetUserScreen(-1, 1)

will produce

...whatever...

1

2

3

C:\...
1Help   2UserMn
panel.GetUserScreen()
mf.printconsole("1\n")
mf.printconsole("2\n")
mf.printconsole("3\n")
panel.SetUserScreen()

will produce

...whatever...

1

2

3

C:\...
1Help   2UserMn
panel.GetUserScreen()
win.WriteConsole("1")
win.WriteConsole("2")
win.WriteConsole("3")
panel.SetUserScreen()

will produce

...whatever...

123

C:\...
1Help   2UserMn
panel.GetUserScreen(-1, 1)
win.WriteConsole("1")
win.WriteConsole("1")
win.WriteConsole("1")
panel.SetUserScreen(-1, 1)

will produce

...whatever...
123
C:\...
1Help   2UserMn

alabuzhev avatar May 26 '25 13:05 alabuzhev

mf.printconsole("1")

I believe that this should add new line on its own, without explicit "\n", so

--panel.GetUserScreen()
mf.printconsole("1")
mf.printconsole("2")
mf.printconsole("3")
--panel.SetUserScreen()

should produce

...whatever...

1
2
3

C:\...
1Help   2UserMn

regardless of explicit (and nested) [GS]etUserScreen calls.

If user really needs "123", he should use win.WriteConsole directly (and take care of explicit [GS]etUserScreen)

panel.GetUserScreen() should not add anything. panel.SetUserScreen() should not add anything itself, but when it isused in script run from command line, then Far is responsible to add extra line.

If that is possible, then no extra arguments required.

johnd0e avatar May 26 '25 20:05 johnd0e

printconsole is [getuserscreen + writeconsole + setuserscreen], I used it here as a convenient example.

When calls are not nested we for obvious reasons do not know that after "1" you might write "2", so we have to assume that it's over and move the output up to make space for the UI. In other words, empty lines are inevitable. If you don't want them - use nesting or pass 1 as Param1.

How it should work, in my opinion

alabuzhev avatar May 26 '25 21:05 alabuzhev

When calls are not nested we for obvious reasons do not know that after "1" you might write "2"

But we could defer empty line adding until Far finally gets control back, couldn't we? In my imagination this is so simple:

  1. panel.SetUserScreen() only sets some flag
  2. Far checks that flag and makes sure that there is empty line, otherwise adds it.

johnd0e avatar May 26 '25 21:05 johnd0e

"Far gets control back" is not so simple. It can get it back after the plugin exits the exported function (they are legion), or when a plugin calls a function from Far (they are also legion). I don't feel like checking this flag in a hundred places and see no point in general: as mentioned, you can disable them altogether and add (or not) your own breaks wherever you like.

alabuzhev avatar May 26 '25 21:05 alabuzhev

6483

panel.GetUserScreen!
mf.printconsole("1")
mf.printconsole("2")
mf.printconsole("3")
panel.SetUserScreen!

> 123

Roughly mf.printconsole should be equivalent of standard print, which always adds newline. Here it can be easily fixed locally for mf.printconsole alone.

But I would prefer to have more general solution, that would work for cases like this:

local function print (str)
  panel.GetUserScreen()
  win.WriteConsole(str.."\n")
  panel.SetUserScreen()
end

panel.GetUserScreen()
print(1)
print(2)
print(3)
panel.SetUserScreen()

johnd0e avatar May 27 '25 11:05 johnd0e

> 123

Good news: it is consistent with my example. Bad news: my example is inconsistent with my description. Updated & 5484.

alabuzhev avatar May 27 '25 16:05 alabuzhev

with my description.

Your description still looks confusing to me. Please define meaning of Get/SetUserScreen 2nd argument.

johnd0e avatar May 27 '25 16:05 johnd0e

meaning of Get/SetUserScreen 2nd argument.

So it roughly means "do not ensure empty line at the end of output". But wouldn't that be enough for SetUserScreen alone?

johnd0e avatar May 28 '25 18:05 johnd0e

Backwards compatibility.

alabuzhev avatar May 28 '25 19:05 alabuzhev

Ok, than what are your suggestions regarding mf.printconsole? https://github.com/FarGroup/FarManager/blob/f23ad5f43b202453fe7d3c233983ca36246f6806/plugins/luamacro/api.lua#L624-L631 How to make it output without empty lines (considering that we still need empty line before prompt)?

johnd0e avatar Jun 09 '25 17:06 johnd0e

How to make it output without empty lines

  1. Pass 1 to Get and Set
  2. Add \n wherever it makes sense for you.

alabuzhev avatar Jun 09 '25 18:06 alabuzhev

The ideal solution would be if user not required to care about final \n. Any idea how to implement this?

Edit In either case currently we cannot make use of the second argument GET/SETUSERSCREEN for mf.printconsole.

johnd0e avatar Jun 09 '25 19:06 johnd0e

After upgrading to b6488 from before the changes for this issue, when I run a CLI command from AltHistory (e.g. ver), the command itself is not printed, only its output, which makes the scrollback much less useful. Can this scenario be fixed within FAR? P.S. AFAICS AltHistory uses macros somehow to run the commands.

HamRusTal avatar Jun 10 '25 16:06 HamRusTal

Any idea how to implement this?

As I explained earlier, it's hard to tell which one is "final".

Can this scenario be fixed within FAR?

Thanks for reporting. 6490.

alabuzhev avatar Jun 10 '25 20:06 alabuzhev

Thanks for reporting. 6490.

Thank you! With regular CLI commands, this is fixed. But I noticed that (regardless of AltHistory) commands with plugin prefixes (such as edit: < cmd /? or lm:load) are not printed. Far's own far:about and far:config are fine but far:regex is not — it's messy.

HamRusTal avatar Jun 11 '25 20:06 HamRusTal

commands with plugin prefixes (such as edit: < cmd /? or lm:load) are not printed

It's always (or at least for a very long time) been like that. The reason is simple: calling plugin by prefix just calls a plugin's function, and this usually does not print anything, so no need to prepare the canvas etc.

it's messy

Same logic applies: if a command prints something, it should echo, otherwise not.

far:about

This one does print, so it should echo as well.

far:regex

This doesn't print anything.

far:config

This also doesn't print anything, so it should not hide panels, print echo etc., same as far:regex. Probably a bug. 6491.

alabuzhev avatar Jun 11 '25 21:06 alabuzhev

logic applies: if a command prints something, it should echo, otherwise not

First and foremost, I question that this logic is useful. I'd like to be able to recollect from the scrollback what silent commands I ran among the verbose ones and with what arguments I called them. After all, many nix-style commands print nothing when successful (and print something otherwise).

Next, there's still an inconsistency: if I run some well-behaved GUI program (such as notepad.exe MyFile.txt or just notepad), the program does not print anything to the console, yet the command is printed by Far. It is exactly what I expect from commands-with-prefixes too.

P.S. It looks like any (not just GUI) external program invocation command is printed, regardless of its output. I see no reason why the plugin prefixed commands should be of less value, especially considering that they can have complex arguments.

HamRusTal avatar Jun 12 '25 00:06 HamRusTal

@HamRusTal I was about to explain why it won't work, but then I realized that it might, so 6492. Please raise a new issue if you have further comments or observations.

alabuzhev avatar Jun 12 '25 16:06 alabuzhev