263 lines
8.8 KiB
Plaintext
263 lines
8.8 KiB
Plaintext
@implements IAsyncDisposable
|
|
|
|
@using Microsoft.AspNetCore.Components
|
|
@using Microsoft.CodeAnalysis
|
|
@using Microsoft.CodeAnalysis.Text
|
|
@using Microsoft.JSInterop
|
|
@using RobotNet.WebApp.Scripts.Monaco
|
|
@using RobotNet.WebApp.Scripts.Monaco.Editor
|
|
@using RobotNet.WebApp.Scripts.Monaco.Languages
|
|
@using System.Timers
|
|
|
|
@inject IJSRuntime JsRuntime
|
|
@inject ISnackbar Snackbar
|
|
|
|
<div class="w-100 h-100 d-flex flex-column" tabindex="0" @onkeyup:preventDefault @onkeyup:stopPropagation @onkeyup="OnKeyPress">
|
|
<div class="w-100 d-flex align-items-center justify-content-between">
|
|
<div></div>
|
|
<div>@NameFile</div>
|
|
<div></div>
|
|
</div>
|
|
<div @ref=EditorContainer class="flex-grow-1 w-100"></div>
|
|
</div>
|
|
|
|
|
|
@code {
|
|
[CascadingParameter]
|
|
private ScriptWorkspace Workspace { get; set; } = null!;
|
|
|
|
private object dotNetHelper = default!;
|
|
private IJSInProcessObjectReference? editor;
|
|
private IJSInProcessObjectReference? editorModel;
|
|
private IJSInProcessObjectReference? disposableCompletionItemProvider;
|
|
private IJSInProcessObjectReference? disposableSignatureHelpProvider;
|
|
private IJSInProcessObjectReference? disposableHoverProvider;
|
|
|
|
private bool IsEditorInitialized;
|
|
private string Code = "";
|
|
private string NameFile = "";
|
|
public ProcessorState State { get; private set; } = ProcessorState.Idle;
|
|
private bool IsReadOnly = true;
|
|
|
|
private ElementReference EditorContainer;
|
|
|
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
|
{
|
|
await base.OnAfterRenderAsync(firstRender);
|
|
if (firstRender)
|
|
{
|
|
dotNetHelper = DotNetObjectReference.Create(this);
|
|
|
|
editor = await JsRuntime.InvokeAsync<IJSInProcessObjectReference>("monaco.editor.create", EditorContainer, new
|
|
{
|
|
Value = "",
|
|
Language = "csharp",
|
|
Theme = "vs-dark",
|
|
GlyphMargin = true,
|
|
AutomaticLayout = true,
|
|
ReadOnly = IsReadOnly,
|
|
});
|
|
|
|
editorModel = await editor.InvokeAsync<IJSInProcessObjectReference>("getModel");
|
|
|
|
await JsRuntime.InvokeVoidAsync("MonacoRuntime.OnCodeEditorDidChangeModelContent", editor, dotNetHelper, nameof(CodeEditorDidChangeModelContent));
|
|
disposableCompletionItemProvider = await JsRuntime.InvokeAsync<IJSInProcessObjectReference>("MonacoRuntime.CSharpLanguageRegisterCompletionItemProvider", dotNetHelper, nameof(GetCompletionItems));
|
|
disposableSignatureHelpProvider = await JsRuntime.InvokeAsync<IJSInProcessObjectReference>("MonacoRuntime.CSharpLanguageRegisterSignatureHelpProvider", dotNetHelper, nameof(GetSignatureHelp));
|
|
disposableHoverProvider = await JsRuntime.InvokeAsync<IJSInProcessObjectReference>("MonacoRuntime.CSharpLanguageRegisterHoverProvider", dotNetHelper, nameof(GetQuickInfo));
|
|
IsEditorInitialized = true;
|
|
|
|
Workspace.OnFileChanged += FileChanged;
|
|
Workspace.OnDiagnoticChanged += DiagnoticChanged;
|
|
}
|
|
}
|
|
|
|
public void OnProcessStateChanged(ProcessorState state)
|
|
{
|
|
State = state;
|
|
var isReadOnly = State != ProcessorState.Ready && State != ProcessorState.Idle && State != ProcessorState.BuildError;
|
|
if (isReadOnly != IsReadOnly)
|
|
{
|
|
IsReadOnly = isReadOnly;
|
|
if (editor != null)
|
|
{
|
|
_ = InvokeAsync(async Task () =>
|
|
{
|
|
await editor.InvokeVoidAsync("updateOptions", new
|
|
{
|
|
ReadOnly = IsReadOnly,
|
|
});
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task FileChanged(ScriptFileModel? file)
|
|
{
|
|
await this.InvokeAsync(async Task () =>
|
|
{
|
|
if (file is null)
|
|
{
|
|
NameFile = "";
|
|
Code = "";
|
|
}
|
|
else
|
|
{
|
|
NameFile = file.Name;
|
|
Code = file.EditCode;
|
|
}
|
|
if (IsEditorInitialized && editor is not null)
|
|
{
|
|
await editor.InvokeVoidAsync("setValue", Code);
|
|
await JsRuntime.InvokeVoidAsync("monaco.editor.setModelMarkers", editorModel, "default", file?.Diagnostics.Select(ToMonacoDiagnostic) ?? []);
|
|
// await editor.InvokeVoidAsync("updateOptions", new
|
|
// {
|
|
// ReadOnly = IsReadOnly,
|
|
// });
|
|
}
|
|
StateHasChanged();
|
|
});
|
|
}
|
|
|
|
private async Task DiagnoticChanged(IEnumerable<Diagnostic> diagnostics)
|
|
{
|
|
await this.InvokeAsync(async Task () =>
|
|
{
|
|
if (IsEditorInitialized && editor is not null)
|
|
{
|
|
await JsRuntime.InvokeVoidAsync("monaco.editor.setModelMarkers", editorModel, "default", diagnostics.Select(ToMonacoDiagnostic) ?? []);
|
|
}
|
|
});
|
|
|
|
}
|
|
|
|
[JSInvokable]
|
|
public async Task<CompletionList> GetCompletionItems(int line, int column, int kind, char? triggerCharacter)
|
|
{
|
|
return new()
|
|
{
|
|
Suggestions = [.. await Workspace.GetCompletionCurrentFileAsync(line, column, kind, triggerCharacter)],
|
|
};
|
|
}
|
|
|
|
[JSInvokable]
|
|
public async Task<string?> GetQuickInfo(int line, int column)
|
|
{
|
|
return await Workspace.GetQuickInfoCurrentFile(line, column);
|
|
}
|
|
|
|
[JSInvokable]
|
|
public async Task<SignatureHelpResult?> GetSignatureHelp(int line, int column)
|
|
{
|
|
return await Workspace.GetSignatureHelpCurrentFile(line, column);
|
|
}
|
|
|
|
[JSInvokable]
|
|
public async Task CodeEditorDidChangeModelContent(ModelContentChangedEvent e)
|
|
{
|
|
if (e.IsFlush) return;
|
|
|
|
if (e.Changes.Length != 0)
|
|
{
|
|
if (editor is not null)
|
|
{
|
|
var code = await editor.InvokeAsync<string>("getValue");
|
|
|
|
if (code != Code)
|
|
{
|
|
Code = code;
|
|
Workspace.WriteDocument(Code);
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
bool isSaving = false;
|
|
private async Task OnKeyPress(KeyboardEventArgs e)
|
|
{
|
|
if ((e.Key == "s" || e.Key == "S") && e.CtrlKey)
|
|
{
|
|
if(IsReadOnly)
|
|
{
|
|
Snackbar.Add($"Hệ thống đang ở trạng thái {State}, Không thể thay đổi script", Severity.Warning);
|
|
return;
|
|
}
|
|
if (isSaving) return;
|
|
isSaving = true;
|
|
var result = await Workspace.SaveCurrentFileAsync();
|
|
if (!result.IsSuccess)
|
|
{
|
|
Snackbar.Add(result.Message, Severity.Error);
|
|
}
|
|
isSaving = false;
|
|
}
|
|
}
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
if (disposableCompletionItemProvider != null)
|
|
{
|
|
await disposableCompletionItemProvider.InvokeVoidAsync("dispose");
|
|
await disposableCompletionItemProvider.DisposeAsync();
|
|
disposableCompletionItemProvider = null;
|
|
}
|
|
|
|
if (disposableSignatureHelpProvider != null)
|
|
{
|
|
await disposableSignatureHelpProvider.InvokeVoidAsync("dispose");
|
|
await disposableSignatureHelpProvider.DisposeAsync();
|
|
disposableSignatureHelpProvider = null;
|
|
}
|
|
|
|
if (disposableHoverProvider != null)
|
|
{
|
|
await disposableHoverProvider.InvokeVoidAsync("dispose");
|
|
await disposableHoverProvider.DisposeAsync();
|
|
disposableHoverProvider = null;
|
|
}
|
|
|
|
if (editorModel != null)
|
|
{
|
|
await editorModel.InvokeVoidAsync("dispose");
|
|
await editorModel.DisposeAsync();
|
|
editorModel = null;
|
|
}
|
|
|
|
if (editor != null)
|
|
{
|
|
await editor.InvokeVoidAsync("dispose");
|
|
await editor.DisposeAsync();
|
|
editor = null;
|
|
}
|
|
|
|
if (IsEditorInitialized)
|
|
{
|
|
Workspace.OnFileChanged -= FileChanged;
|
|
Workspace.OnDiagnoticChanged -= DiagnoticChanged;
|
|
}
|
|
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
|
|
private static MarkerData ToMonacoDiagnostic(Diagnostic diagnostic)
|
|
{
|
|
var lineSpan = diagnostic.Location.GetLineSpan();
|
|
return new()
|
|
{
|
|
StartLineNumber = lineSpan.StartLinePosition.Line + 1,
|
|
StartColumn = lineSpan.StartLinePosition.Character + 1,
|
|
EndLineNumber = lineSpan.EndLinePosition.Line + 1,
|
|
EndColumn = lineSpan.EndLinePosition.Character + 1,
|
|
Message = diagnostic.GetMessage(),
|
|
Severity = diagnostic.Severity switch
|
|
{
|
|
DiagnosticSeverity.Info => MarkerSeverity.Info,
|
|
DiagnosticSeverity.Warning => MarkerSeverity.Warning,
|
|
DiagnosticSeverity.Error => MarkerSeverity.Error,
|
|
_ => MarkerSeverity.Hint,
|
|
},
|
|
};
|
|
}
|
|
}
|