Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CSharpRepl.Services/CSharpRepl.Services.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@
-->
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.9" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.9" />
<PackageReference Include="Microsoft.SymbolStore" Version="1.0.727501" />
<PackageReference Include="Microsoft.SymbolStore" Version="1.0.731401" />
<PackageReference Include="OpenAI" Version="2.11.0" />
<PackageReference Include="PrettyPrompt" Version="6.0.1" />
<PackageReference Include="PrettyPrompt" Version="6.0.2" />
<PackageReference Include="Spectre.Console.Cli" Version="0.55.0" />
<PackageReference Include="Spectre.Console.Ansi" Version="0.57.0" />
<PackageReference Include="System.IO.Abstractions" Version="22.1.1" />
Expand Down
140 changes: 70 additions & 70 deletions CSharpRepl.Services/SyntaxHighlighting/HighlightedSpan.cs
Original file line number Diff line number Diff line change
@@ -1,71 +1,71 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.Text;
using PrettyPrompt.Highlighting;
namespace CSharpRepl.Services.SyntaxHighlighting;
public sealed record HighlightedSpan(TextSpan TextSpan, AnsiColor Color);
public static class HighlightedSpanExtensions
{
public static IReadOnlyCollection<FormatSpan> ToFormatSpans(this IReadOnlyCollection<HighlightedSpan> spans) => spans
.Select(span => new FormatSpan(
span.TextSpan.Start,
span.TextSpan.Length,
new ConsoleFormat(Foreground: span.Color)
))
.ToArray();
/// <summary>
/// Converts possibly-overlapping format spans into the disjoint spans <see cref="FormattedString"/> requires.
/// Roslyn's classifier nests spans (e.g. a string-escape-character span inside a string-literal span). The
/// later-starting span is the more specific classification, so it wins; the span it interrupts resumes after
/// it ends. The prompt's own renderer tolerates overlap, so this is only needed when building a FormattedString.
/// </summary>
public static IReadOnlyList<FormatSpan> FlattenOverlappingSpans(this IEnumerable<FormatSpan> spans)
{
// at equal starts, the longer (enclosing) span sorts first so the shorter, more specific span is
// pushed later and takes precedence over it.
var sorted = spans.OrderBy(s => s.Start).ThenByDescending(s => s.Length).ToArray();
var result = new List<FormatSpan>(sorted.Length);
var enclosing = new Stack<FormatSpan>();
int position = 0;
foreach (var span in sorted)
{
// emit the tails of spans that end before this one starts
while (enclosing.TryPeek(out var top) && top.End <= span.Start)
{
Emit(top, top.End);
enclosing.Pop();
}
// emit the segment of the still-open span leading up to this more specific span
if (enclosing.TryPeek(out var current))
{
Emit(current, span.Start);
}
enclosing.Push(span);
}
while (enclosing.TryPop(out var top))
{
Emit(top, top.End);
}
return result;
void Emit(FormatSpan span, int end)
{
int start = Math.Max(position, span.Start);
if (end > start)
{
result.Add(new FormatSpan(start, end - start, span.Formatting));
position = end;
}
}
}
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.Text;
using PrettyPrompt.Highlighting;

namespace CSharpRepl.Services.SyntaxHighlighting;

public sealed record HighlightedSpan(TextSpan TextSpan, AnsiColor Color);

public static class HighlightedSpanExtensions
{
public static IReadOnlyCollection<FormatSpan> ToFormatSpans(this IReadOnlyCollection<HighlightedSpan> spans) => spans
.Select(span => new FormatSpan(
span.TextSpan.Start,
span.TextSpan.Length,
new ConsoleFormat(Foreground: span.Color)
))
.ToArray();

/// <summary>
/// Converts possibly-overlapping format spans into the disjoint spans <see cref="FormattedString"/> requires.
/// Roslyn's classifier nests spans (e.g. a string-escape-character span inside a string-literal span). The
/// later-starting span is the more specific classification, so it wins; the span it interrupts resumes after
/// it ends. The prompt's own renderer tolerates overlap, so this is only needed when building a FormattedString.
/// </summary>
public static IReadOnlyList<FormatSpan> FlattenOverlappingSpans(this IEnumerable<FormatSpan> spans)
{
// at equal starts, the longer (enclosing) span sorts first so the shorter, more specific span is
// pushed later and takes precedence over it.
var sorted = spans.OrderBy(s => s.Start).ThenByDescending(s => s.Length).ToArray();
var result = new List<FormatSpan>(sorted.Length);
var enclosing = new Stack<FormatSpan>();
int position = 0;

foreach (var span in sorted)
{
// emit the tails of spans that end before this one starts
while (enclosing.TryPeek(out var top) && top.End <= span.Start)
{
Emit(top, top.End);
enclosing.Pop();
}
// emit the segment of the still-open span leading up to this more specific span
if (enclosing.TryPeek(out var current))
{
Emit(current, span.Start);
}
enclosing.Push(span);
}
while (enclosing.TryPop(out var top))
{
Emit(top, top.End);
}
return result;

void Emit(FormatSpan span, int end)
{
int start = Math.Max(position, span.Start);
if (end > start)
{
result.Add(new FormatSpan(start, end - start, span.Formatting));
position = end;
}
}
}
}
4 changes: 2 additions & 2 deletions CSharpRepl/CSharpRepl.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="PrettyPrompt" Version="6.0.1" />
<PackageReference Include="System.CommandLine" Version="3.0.0-preview.4.26230.115" />
<PackageReference Include="PrettyPrompt" Version="6.0.2" />
<PackageReference Include="System.CommandLine" Version="3.0.0-preview.5.26302.115" />
<PackageReference Include="System.Reflection.MetadataLoadContext" Version="10.0.9" />
<!-- Keep MSBuild/StringTools assemblies out of the output dir; they're loaded from the SDK via
MSBuildLocator at runtime. Required transitively through CSharpRepl.Services.
Expand Down
2 changes: 1 addition & 1 deletion Tests/CSharpRepl.Tests/CSharpRepl.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

<ItemGroup>
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="PrettyPrompt" Version="6.0.1" />
<PackageReference Include="PrettyPrompt" Version="6.0.2" />
<PackageReference Include="Spectre.Console.Testing" Version="0.57.0" />
<PackageReference Include="System.IO.Abstractions.TestingHelpers" Version="22.1.1" />
<PackageReference Include="xunit.v3.mtp-v2" Version="4.0.0-pre.128" />
Expand Down
Loading