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

cmd.get_errorlevel can break print (same underlying issue as #93) #134

Closed
pukkandan opened this issue Jun 24, 2021 · 7 comments
Closed

cmd.get_errorlevel can break print (same underlying issue as #93) #134

pukkandan opened this issue Jun 24, 2021 · 7 comments
Labels
bug Something isn't working

Comments

@pukkandan
Copy link

pukkandan commented Jun 24, 2021

Hi,
First of all, I would like thank you for maintaining this project. I knew of fancy terminal emulators like cmder/ConEmu, but never imagined an app that can accomplish so much by injecting into the native cmd. I've only been using this for a few days now, but I can already see this becoming one of those tools that I just cant live without.

Now, to the issue:


I was trying to use the clink.onbeginedit and onendedit hooks as mentioned in #131. It initially worked well. But after a few commands, it spontaneously stops working. And when it does, other scripts seems to stop working as well. Here's my (test) script:

local i = 0
print('start')

clink.onbeginedit(function ()
  print('begin')
end)

clink.onendedit(function (curr_line)
  print('end')
end)


function test(rl_buffer)
    rl_buffer:beginoutput()
    print('test')
end

local prompt = clink.promptfilter(100)
function prompt:filter(prompt)
    i = i+1
    return i.." $ "
end

and here's the settings:

# name: Trick CMD into revealing the last exit code
# type: boolean
cmd.get_errorlevel = True

# name: Enables Lua debugging
# type: boolean
lua.debug = True

I am running on the latest release 1.2.15 with a fresh profile. The issue can be reproduced in both normal cmd and Windows Terminal (Preview)

Microsoft Windows [Version 10.0.19043.1052]
(c) Microsoft Corporation. All rights reserved.

Clink v1.2.15.b4efda
Copyright (c) 2012-2018 Martin Ridgers
Portions Copyright (c) 2020-2021 Christopher Antos
https://github.com/chrisant996/clink

start
begin
1 $ :: Pressing Ctrl+R which is registered to clink-reload.end

start
begin
1 $ :: Next pressing Ctrl+T registerd to "luafunc:test".
test
1 $ echo.some random command.
end
some random command.

begin
2 $ echo.some random command.
end
some random command.

begin
3 $ echo.some random command.
end
some random command.

4 $ :: You can see begin didnt appear.
5 $ :: Pressing Ctrl+R again.
1 $ :: Prompt reset, but "start" didnt print.
2 $ :: Trying Ctrl+T again.2 $ :: Trying Ctrl+T again.

In the test above, you can see the hooks stopped working after 3 commands. Most of the time, it stays up for longer though. I just got lucky (or unlucky?) this time

Once the issue appears, when trying to reload, the prompt gets reloaded, but "start" was not printed and the hooks were not restored. Trying to run the "test" function afterwards also did not produce any output

Please let me know if any other information is needed. And thanks in advance for your time

@pukkandan
Copy link
Author

pukkandan commented Jun 24, 2021

I didn't think of this originally, but it actually seems only print is affected.

I tried replacing test with this:

function test(rl_buffer)
    rl_buffer:beginoutput()
    os.execute('cmd /c echo test')
end

Now Ctrl+T does print test even after the issue starts. Similarly, putting the same os.execute into the other functions works around the issue completely (although with terrible performance).

So it seems the hooks somehow breaks print, but nothing else...

@pukkandan pukkandan changed the title clink.onbeginedit and onendedit not triggering clink.onbeginedit and onendedit breaks print Jun 24, 2021
@chrisant996
Copy link
Owner

It appears to be a side effect of cmd.get_errorlevel being true, and is exactly the same issue as #93.

CMD internally handles redirection in a way that is functionally reasonable, but which means the internal actual stdout handle can change whenever redirection is used. And since turning on cmd.get_errorlevel means in between every prompt Clink tells CMD that the user ran echo %errorlevel% > {clink_profile_path}\clink_errorlevel.txt, there's a lot of redirection happening.

The simplest workaround is

print = clink.print

but there are probably other things besides print() that are affected as well.

I originally made the fix for #93 run only once per session. But apparently the issue can recur again later on, so it looks like it will be necessary to repeatedly run the fix code.

@chrisant996
Copy link
Owner

chrisant996 commented Jun 24, 2021

Great find -- thank you for reporting it!

Also, thank you for clear descriptions, troubleshooting, and sharing the subtle observation that only print() is affected - that saved me a lot of time and I was able to quickly verify the root cause.

@chrisant996 chrisant996 changed the title clink.onbeginedit and onendedit breaks print cmd.get_errorlevel can break print (same underlying issue as #93) Jun 24, 2021
@chrisant996 chrisant996 added the bug Something isn't working label Jun 24, 2021
@pukkandan
Copy link
Author

pukkandan commented Jun 24, 2021

Thanks for the quick fix ❤️

I was looking at issues related to onendedit since I didn't encounter the issue before that script and thus missed #93. Looking at the new changes, I see you added this to the docs: cb33f8f#diff-85ed67821337d5a38659322650b33f0800def9c3ca84a584c59b7bd0bc6c65cdR112-R114

Because onendedit can be stopped, a script must be prepared to receive
onbeginedit but not receive a corresponding onendedit.

Is this referring to a function's return value blocking another function, or is there some other case where the hooks are not called?


Btw, I have some small feature requests/bug reports. Would you prefer I open a seperate issue for each or a single one explaining all of them?

@chrisant996
Copy link
Owner

chrisant996 commented Jun 24, 2021

Because onendedit can be stopped, a script must be prepared to receive
onbeginedit but not receive a corresponding onendedit.

Is this referring to a function's return value blocking another function, or is there some other case where the hooks are not called?

Both. The onendedit event does two things:

  1. Signals end of editing.
  2. Lets a script replace the edited line.
  • Because of 2, it needs to be cancelable.
  • Because of cancelable 1 is not reliable.
  • And because of 2 there are cases where Clink doesn't want to call onendedit -- for example after expanding a doskey macro and I think another case or two where Clink has already done special processing of the input line and doesn't want to let a script alter the behavior. Update: I misremembered -- Clink does not currently skip onendedit unless a script tells it to stop.

So, onendedit ends up not fully reliable as an "editing ended" indicator if a script needs to strictly know every time editing ends.

I'm deferring exploring what to do about that, but I wanted to at least document that it isn't guaranteed to be called.

Maybe I'll make the event happen each time, but make the stopping apply only to the part about replacing the input line?

Btw, I have some small feature requests/bug reports. Would you prefer I open a seperate issue for each or a single one explaining all of them?

I don't mind either way, but if they're separate they can be tracked/closed independently, and they can be searched independently by others.

Depending on how related the issues are, it may or may not be useful to separate them.

@chrisant996
Copy link
Owner

I'm going to split onendedit into two separate events:

  1. onendedit(command_line) -- this will only signal end of editing, and will be reliably paired with onbeginedit.
  2. a second yet-to-be-named event that lets a script replace the edited line.

That will be a breaking change, but it should be very rare that onendedit is being used to replace the edited line, so the impact should be minimal.

@pukkandan
Copy link
Author

Update: I misremembered -- Clink does not currently skip onendedit unless a script tells it to stop.

This is what I was wondering. I understand that other functions can cancel the rest of the chain. But as long as clink itself doesnt do it, its good for me (and assume for most people). If the cancellation only happens because of user scripts, we can always make sure that the functions are registered in the correct order to produce expected behaviour.

I'm going to split onendedit into two separate events

I really havent use this tool enough to comment on this. I'm sure you know what you are doing :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants
-