display run logs in terminal
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Threading.Channels;
|
||||||
using Ardalis.GuardClauses;
|
using Ardalis.GuardClauses;
|
||||||
using AsyncReadProcess;
|
using AsyncReadProcess;
|
||||||
using SharpIDE.Application.Features.Events;
|
using SharpIDE.Application.Features.Events;
|
||||||
@@ -31,21 +32,28 @@ public class RunService
|
|||||||
RedirectStandardError = true
|
RedirectStandardError = true
|
||||||
};
|
};
|
||||||
|
|
||||||
var process = new AsyncReadProcess.Process2
|
var process = new Process2
|
||||||
{
|
{
|
||||||
StartInfo = processStartInfo
|
StartInfo = processStartInfo
|
||||||
};
|
};
|
||||||
|
|
||||||
process.Start();
|
process.Start();
|
||||||
|
|
||||||
|
project.RunningOutputChannel = Channel.CreateUnbounded<string>(new UnboundedChannelOptions
|
||||||
|
{
|
||||||
|
SingleReader = true,
|
||||||
|
SingleWriter = false,
|
||||||
|
});
|
||||||
var logsDrained = new TaskCompletionSource();
|
var logsDrained = new TaskCompletionSource();
|
||||||
_ = Task.Run(async () =>
|
_ = Task.Run(async () =>
|
||||||
{
|
{
|
||||||
await foreach(var log in process.CombinedOutputChannel.Reader.ReadAllAsync())
|
await foreach(var log in process.CombinedOutputChannel.Reader.ReadAllAsync())
|
||||||
{
|
{
|
||||||
var logString = System.Text.Encoding.UTF8.GetString(log, 0, log.Length);
|
var logString = System.Text.Encoding.UTF8.GetString(log, 0, log.Length);
|
||||||
Console.Write(logString);
|
//Console.Write(logString);
|
||||||
|
await project.RunningOutputChannel.Writer.WriteAsync(logString).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
project.RunningOutputChannel.Writer.Complete();
|
||||||
logsDrained.TrySetResult();
|
logsDrained.TrySetResult();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -53,6 +61,7 @@ public class RunService
|
|||||||
project.OpenInRunPanel = true;
|
project.OpenInRunPanel = true;
|
||||||
GlobalEvents.InvokeProjectsRunningChanged();
|
GlobalEvents.InvokeProjectsRunningChanged();
|
||||||
GlobalEvents.InvokeStartedRunningProject();
|
GlobalEvents.InvokeStartedRunningProject();
|
||||||
|
project.InvokeProjectStartedRunning();
|
||||||
await process.WaitForExitAsync().WaitAsync(project.RunningCancellationTokenSource.Token).ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing);
|
await process.WaitForExitAsync().WaitAsync(project.RunningCancellationTokenSource.Token).ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing);
|
||||||
if (project.RunningCancellationTokenSource.IsCancellationRequested)
|
if (project.RunningCancellationTokenSource.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Microsoft.Build.Evaluation;
|
using System.Threading.Channels;
|
||||||
|
using Microsoft.Build.Evaluation;
|
||||||
|
|
||||||
namespace SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
|
namespace SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
|
||||||
|
|
||||||
@@ -37,4 +38,7 @@ public class SharpIdeProjectModel : ISharpIdeNode
|
|||||||
|
|
||||||
public bool IsRunnable => MsBuildEvaluationProject.Xml.Sdk is "Microsoft.NET.Sdk.BlazorWebAssembly" || MsBuildEvaluationProject.GetPropertyValue("OutputType") is "Exe" or "WinExe";
|
public bool IsRunnable => MsBuildEvaluationProject.Xml.Sdk is "Microsoft.NET.Sdk.BlazorWebAssembly" || MsBuildEvaluationProject.GetPropertyValue("OutputType") is "Exe" or "WinExe";
|
||||||
public bool OpenInRunPanel { get; set; }
|
public bool OpenInRunPanel { get; set; }
|
||||||
|
public Channel<string>? RunningOutputChannel { get; set; }
|
||||||
|
public event Func<Task> ProjectStartedRunning = () => Task.CompletedTask;
|
||||||
|
public void InvokeProjectStartedRunning() => ProjectStartedRunning?.Invoke();
|
||||||
}
|
}
|
||||||
|
|||||||
106
src/SharpIDE.Photino/Components/RunOutputDisplay.razor
Normal file
106
src/SharpIDE.Photino/Components/RunOutputDisplay.razor
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
@using Ardalis.GuardClauses
|
||||||
|
@using SharpIDE.Application.Features.Build
|
||||||
|
@using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence
|
||||||
|
@using XtermBlazor
|
||||||
|
|
||||||
|
@inject BuildService BuildService
|
||||||
|
|
||||||
|
@implements IDisposable
|
||||||
|
<style>
|
||||||
|
.xterm-underline-5.xterm-underline-5 { /* Specificity hack lol */
|
||||||
|
text-decoration: dotted underline;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div style="width: 100%; height: 100%; overflow: visible">
|
||||||
|
<Xterm @ref="@_terminalRef" Options="@_options" Style="height: calc(100% - 1px)" Addons="@_addons" OnFirstRender="@OnFirstRender"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
[Parameter, EditorRequired]
|
||||||
|
public SharpIdeProjectModel Project { get; set; } = null!;
|
||||||
|
|
||||||
|
private Xterm _terminalRef;
|
||||||
|
|
||||||
|
private readonly TerminalOptions _options = new TerminalOptions
|
||||||
|
{
|
||||||
|
CursorBlink = true,
|
||||||
|
CursorStyle = CursorStyle.Bar,
|
||||||
|
Columns = 140,
|
||||||
|
FontFamily = "Cascadia Code",
|
||||||
|
FontWeightBold = "400",
|
||||||
|
Theme =
|
||||||
|
{
|
||||||
|
BrightGreen = "#98c379",
|
||||||
|
BrightRed = "#e06c75",
|
||||||
|
Foreground = "#dcdfe4",
|
||||||
|
Background = "#282c34",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
private HashSet<string> _addons = ["addon-fit"];
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
Project.ProjectStartedRunning += OnProjectStartedRunning;
|
||||||
|
// This event may/will be raised before the component is initialized, so we call OnProjectStartedRunning directly, for the first render.
|
||||||
|
await OnProjectStartedRunning();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnProjectStartedRunning()
|
||||||
|
{
|
||||||
|
Guard.Against.Null(Project);
|
||||||
|
Guard.Against.Null(Project.RunningOutputChannel, nameof(Project.RunningOutputChannel));
|
||||||
|
await ClearPreviousOutput();
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await foreach (var log in Project.RunningOutputChannel.Reader.ReadAllAsync())
|
||||||
|
{
|
||||||
|
await _terminalRef.Write(log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
await DispatchExceptionAsync(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ClearPreviousOutput()
|
||||||
|
{
|
||||||
|
if (_terminalRef is not null)
|
||||||
|
{
|
||||||
|
await _terminalRef.Clear();
|
||||||
|
await InvokeAsync(StateHasChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose() => Project.ProjectStartedRunning -= OnProjectStartedRunning;
|
||||||
|
|
||||||
|
private async Task OnFirstRender()
|
||||||
|
{
|
||||||
|
await _terminalRef.Addon("addon-fit").InvokeVoidAsync("fit");
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
await Task.Delay(500).ConfigureAwait(false);
|
||||||
|
await InvokeAsync(async () =>
|
||||||
|
{
|
||||||
|
await _terminalRef.Addon("addon-fit").InvokeVoidAsync("fit");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
await DispatchExceptionAsync(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,14 +11,17 @@
|
|||||||
.lowercase-tab-header {
|
.lowercase-tab-header {
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
}
|
}
|
||||||
|
.panels-full-height {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@* <MudText>Run</MudText> *@
|
@* <MudText>Run</MudText> *@
|
||||||
<MudTabs KeepPanelsAlive="true" TabPanelClass="lowercase-tab-header">
|
<MudTabs Style="height: 100%" KeepPanelsAlive="true" PanelClass="panels-full-height" TabPanelClass="lowercase-tab-header">
|
||||||
<ChildContent>
|
<ChildContent>
|
||||||
@foreach (var tab in OpenTabs)
|
@foreach (var tab in OpenTabs)
|
||||||
{
|
{
|
||||||
<MudTabPanel ID="@tab" Text="@tab.Name">
|
<MudTabPanel Style="height: 100%" ID="@tab" Text="@tab.Name">
|
||||||
Tab content
|
<RunOutputDisplay Project="@tab"/>
|
||||||
</MudTabPanel>
|
</MudTabPanel>
|
||||||
}
|
}
|
||||||
</ChildContent>
|
</ChildContent>
|
||||||
|
|||||||
Reference in New Issue
Block a user