spectreconsole / spectre.console

A .NET library that makes it easier to create beautiful console applications.
https://spectreconsole.net
MIT License
9.17k stars 472 forks source link

Width detection fails when running Windows apps in WSL #216

Open mcon opened 3 years ago

mcon commented 3 years ago

Information

Describe the bug Spectre.Console gets the terminal width by getting System.Console.BufferWidth however, when running a Windows app in WSL, this throws an IOException, leaving Spectre.Console using the default of 80 columns wide whatever the actual size of the terminal.

To Reproduce Run a Spectre.Console app running using the Windows .net core runtime in WSL, log out IAnsiConsole.Width.

Expected behavior It's not straightforward what the best solution to this is. For my purposes, as a workaround, I've just implemented IAnsiConsole, passing through all implementations to AnsiBackend aside from Width which I've hardcoded to a large value - clearly this won't produce desirable behaviour in case of output overflow.

Clearly, it would be possible to do the classic "try moving the cursor by some large amount, check where it is, return it to initial position" trick using escape codes (https://en.wikipedia.org/wiki/ANSI_escape_code#Terminal_output_sequences), however if you're redirecting the output of a Spectre.Console app to a file then this approach isn't going to work well, as Spectre.Console would have to wait for the terminal dimensions to be returned on stdin, which wouldn't happen in the case of a redirect.

It would be possible to time out waiting for terminal dimensions, however that doesn't feel ideal either, as programs would then take longer to run when redirected to file. One "solution" would be to allow the user to hard code the width more easily (e.g. in capabilities), without having to resort to implementing IAnsiConsole boilerplate.

Crucially, the best approach depends on the Spectre.Console philosophy: if the library is meant for applications that only run in the terminal, then terminal escape codes with timeout is probably the way to go, otherwise perhaps overriding Width is better?


Please upvote :+1: this issue if you are interested in it.

patriksvensson commented 3 years ago

@mcon Yes, it's not optimal. I'm currently reworking the capabilities API which will make it possible to override things without creating a new console (as well as implementing specific profiles for known environments). I would suggest that we add some specific (opt-in) logic for more advanced detection once that is in place.

mcon commented 3 years ago

Sounds good to me, certainly I'm not advocating moving away from Console.BufferWidth, and this approach would only be sensible as a fallback.

Frassle commented 3 years ago

Running in mintty has a similar issue. stdout/err aren't console handles because mintty does it's own redirection.

patriksvensson commented 3 years ago

@mcon I revisited this issue, but can't reproduce it. Could you verify if it's working for you now and if not produce a minimal reproducible example?

image

mcon commented 3 years ago

Had a crack in Windows terminal, and was unable to reproduce, but can still see the same issue in my cygwin mintty terminal - probably a misnomer to mention WSL in the initial issue description.

I used the following code which, when using mintty always outputs width of 80 - as I typically user Windows terminal now, the issue doesn't affect me terribly. Thanks for coming back to this one @patriksvensson.

using Spectre.Console;

namespace SpectreConsoleDebug
{
  class Program
  {
    static void Main(string[] args)
    {
      var console = AnsiConsole.Create(new AnsiConsoleSettings());

      console.Write(new Text($"Width: {console.Profile.Out.Width}"));
    }
  }
}
jaredthirsk commented 2 years ago

My scenario: Linux csx script writing stdout to a log file in a cron job.

My workaround to get a custom width: https://gist.github.com/jaredthirsk/4bffdb3fdc3a71dae4515145420c43f2