spectreconsole/spectre.console

Unhandled exception. System.ArgumentOutOfRangeException: Index and length must refer to a location within the string. (Parameter 'length')

Opened this issue ยท 2 comments

Information

  • OS:Windows And Mac
  • Version:0.49.1
  • Terminal: Windows Terminal And Mac MacTerminal

Describe the bug

To Reproduce

Expected behavior
Unhandled exception. System.ArgumentOutOfRangeException: Index and length must refer to a location within the string. (Parameter 'length')
at System.String.ThrowSubstringArgumentOutOfRange(Int32, Int32)
at System.String.Substring(Int32, Int32)
at Spectre.Console.Rendering.Segment.SplitOverflow(Segment, Nullable1, Int32) in /_/src/Spectre.Console/Rendering/Segment.cs:line 371 at Spectre.Console.Paragraph.SplitLines(Int32) in /_/src/Spectre.Console/Widgets/Paragraph.cs:line 232 at Spectre.Console.Paragraph.Render(RenderOptions, Int32) in /_/src/Spectre.Console/Widgets/Paragraph.cs:line 141 at Spectre.Console.TableRenderer.Render(TableRendererContext, List1) in //src/Spectre.Console/Widgets/Table/TableRenderer.cs:line 11
at Spectre.Console.TableRenderer.Render(TableRendererContext, List1) in /_/src/Spectre.Console/Widgets/Table/TableRenderer.cs:line 31 at Spectre.Console.Padder.Render(RenderOptions, Int32) in /_/src/Spectre.Console/Widgets/Padder.cs:line 45 at Spectre.Console.Rendering.LiveRenderable.Render(RenderOptions, Int32)+MoveNext() at System.Collections.Generic.List1.AddRange(IEnumerable1) at Spectre.Console.RenderableExtensions.GetSegments(IAnsiConsole, RenderOptions, IEnumerable1) in /
/src/Spectre.Console/Extensions/RenderableExtensions.cs:line 34
at Spectre.Console.AnsiBuilder.Build(IAnsiConsole, IRenderable) in //src/Spectre.Console/Internal/Backends/Ansi/AnsiBuilder.cs:line 17
at Spectre.Console.AnsiConsoleFacade.Write(IRenderable) in /
/src/Spectre.Console/Internal/Backends/AnsiConsoleFacade.cs:line 38
at Spectre.Console.ProgressRefreshThread.Run() in /_/src/Spectre.Console/Live/Progress/ProgressRefreshThread.cs:line 45
at System.Threading.Thread.StartCallback()

Screenshots

Additional context


Please upvote ๐Ÿ‘ this issue if you are interested in it.

@zwluoqi Thanks for reporting this. We would however need a reproducable example to even know how to fix this and to make sure it doesn't happen again.

@patriksvensson

looking at the source code the posted stack trace, it seems the issue is occurring in Segment.SplitOverflow when the overflow mode is Overflow.Ellipsis or Overflow.Crop and Segment.Text is a string shorter than maxWidth-1.

Which then poses the question as to why the Segment.SplitOverflow method is being called despite the Segment.Text string being shorter than maxWidth-1. Looking at the calling method (Paragraph.SplitLines), its logic decides that Segment.SplitOverflow is being called when Segment.CellCount() is larger than maxWidth.

Ergo, reproducing the problem requires a Segment.Text string whose length is shorter than maxWidth-1 but its corresponding Segment.CellCount() is larger than maxWidth. This condition would be satisfied by strings mainly composed of characters that occupy more than one console cell (like CJK characters for example), rendered by Paragraph elements in either Overflow.Ellipsis or Overflow.Crop mode .

Based on this, i made some quick'n'dirty reproduction code (which i wrote and tested using Spectre.Console 0.49.2-preview.0.16):

using Spectre.Console;
using System.Text;

Console.OutputEncoding = Encoding.UTF8;

int stringWidth = 20;

var table = new Table() { Width = 50 };
table.AddColumn("Foo");
table.AddColumn("Bar");

//
// Adding paragraphs with strings mainly composed of single-cell characters is fine...
//
table.AddRow(
    new Paragraph(new string('a', stringWidth)) { Overflow = Overflow.Ellipsis },
    new Paragraph(new string('b', stringWidth)) { Overflow = Overflow.Ellipsis }
);
AnsiConsole.Write(table);

//
// Exception happens here when adding the paragraphs with the CJK strings to the table
//
table.AddRow(
    new Paragraph(new string('ํ•œ', stringWidth)) { Overflow = Overflow.Ellipsis },
    new Paragraph(new string('๊ธ€', stringWidth)) { Overflow = Overflow.Ellipsis }
);
//
// Using Overflow.Crop would get the same kind of exception
//
// table.AddRow(
//    new Paragraph(new string('ํ•œ', stringWidth)) { Overflow = Overflow.Crop },
//    new Paragraph(new string('๊ธ€', stringWidth)) { Overflow = Overflow.Crop }
// );
AnsiConsole.Write(table);

(dotnetfiddle: https://dotnetfiddle.net/HREpNo)