Files
SzmediTools/Szmedi.RvKits/RvScript/DocumentViewModel.cs
2025-09-16 16:06:41 +08:00

266 lines
8.3 KiB
C#

using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Utils;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Text;
using TextDocument = ICSharpCode.AvalonEdit.Document.TextDocument;
namespace Szmedi.RvKits.RvScript
{
/// <summary>
/// 每个脚本文件视图的ViewModel
/// </summary>
public partial class DocumentViewModel : ObservableObject /*ObservableRecipient, IRecipient<string>*/
{
[ObservableProperty]
private bool _isReadOnly;
RoslynEditorViewModel RoslynEditorViewModel { get; }
public DocumentViewModel(RoslynEditorViewModel roslynEditorViewModel, string FilePath)
{
RoslynEditorViewModel = roslynEditorViewModel;
this.FilePath = FilePath;
Title = FileName;
}
// ...
public DocumentViewModel(RoslynEditorViewModel roslynEditorViewModel)
{
//WeakReferenceMessenger.Default.Register<DocumentViewModel, string, string>(this, "RunScript", (r, m) => r.Receive(m));
RoslynEditorViewModel = roslynEditorViewModel;
IsDirty = true;
Title = FileName;
}
internal void Initialize(DocumentId id)
{
Id = id;
if (Document != null && Document.TextLength > 0)
{
var doc = RoslynEditorViewModel.Host.GetDocument(id);
if (doc != null)
{
RoslynEditorViewModel.Host.UpdateDocument(doc.WithText(SourceText.From(Document.Text)));
IsDirty = false;
}
}
}
internal void OnTextChanged(object sender, EventArgs e)
{
IsDirty = true;
}
public DocumentId Id { get; private set; }
[ObservableProperty]
private TextDocument _document = null;
[ObservableProperty]
private string title = null;
[ObservableProperty]
[NotifyPropertyChangedFor(nameof(FileName))]
[NotifyPropertyChangedFor(nameof(Title))]
private bool isDirty = false;
private string _isReadOnlyReason = string.Empty;
public string IsReadOnlyReason
{
get
{
return _isReadOnlyReason;
}
protected set
{
SetProperty(ref _isReadOnlyReason, value);
}
}
private string _filePath = null;
public string FilePath
{
get { return _filePath; }
set
{
if (_filePath != value)
{
_filePath = value;
OnPropertyChanged(nameof(FilePath));
OnPropertyChanged(nameof(FileName));
OnPropertyChanged(nameof(Title));
if (File.Exists(_filePath))
{
Document = new TextDocument();
IsDirty = false;
IsReadOnly = false;
// Check file attributes and set to read-only if file attributes indicate that
if ((File.GetAttributes(_filePath) & FileAttributes.ReadOnly) != 0)
{
IsReadOnly = true;
IsReadOnlyReason = "无法编辑该文件,另一个进程正在写入\n" +
"如果你想编辑它,请修改访问许可或另存文件到其他位置。";
}
using (FileStream fs = new FileStream(_filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
using (StreamReader reader = FileReader.OpenStream(fs, System.Text.Encoding.UTF8))
{
Document = new TextDocument(reader.ReadToEnd());
}
}
ContentId = _filePath;
}
}
}
}
public string FileName
{
get
{
if (FilePath == null)
return "未命名脚本" + (IsDirty ? "*" : "");
return Path.GetFileName(FilePath) + (IsDirty ? "*" : "");
}
}
[ObservableProperty]
private string _contentId = null;
[ObservableProperty]
private bool _isActive = false;
[ObservableProperty]
private bool _isSelected = false;
public string Text { get; set; }
public bool HasError { get; private set; }
//private static MethodInfo HasSubmissionResult { get; } =
// typeof(Compilation).GetMethod(nameof(HasSubmissionResult), BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
[RelayCommand(CanExecute = nameof(IsDirty))]
private void Save()
{
Text = GetCode();
RoslynEditorViewModel.Save(this, false);
}
private string GetCode()
{
var document = RoslynEditorViewModel.Host.GetDocument(Id);
if (document == null)
return string.Empty;
return document.GetTextAsync()?.Result.ToString();
}
[RelayCommand(CanExecute = nameof(IsDirty))]
private void SaveAs()
{
Text = GetCode();
RoslynEditorViewModel.Save(this, true);
}
[RelayCommand]
private void Close()
{
RoslynEditorViewModel.Close(this);
}
[RelayCommand]
private void Run()
{
Text = GetCode();
RoslynEditorViewModel.Run(this);
}
[RelayCommand]
private void Format()
{
var document = RoslynEditorViewModel.Host.GetDocument(Id);
var formattedDocument = Formatter.FormatAsync(document).Result;
RoslynEditorViewModel.Host.UpdateDocument(formattedDocument);
}
[RelayCommand]
private void Comment()
{
if (RoslynEditorViewModel.CodeEditor == null)
{
return;
}
var roslynEditor = RoslynEditorViewModel.CodeEditor;
var document = roslynEditor.Document;
var startLine = document.GetLineByOffset(roslynEditor.SelectionStart);
var endLine = document.GetLineByOffset(roslynEditor.SelectionStart + roslynEditor.SelectionLength);
document.BeginUpdate();
var line = startLine;
while (line != null && line.LineNumber <= endLine.LineNumber)
{
var whitespace = TextUtilities.GetLeadingWhitespace(document, line);
if (line.Length > whitespace.Length)
{
var text = document.GetText(whitespace) + "//";
document.Replace(whitespace.Offset, whitespace.Length, text, OffsetChangeMappingType.RemoveAndInsert);
}
line = line.NextLine;
}
document.EndUpdate();
}
[RelayCommand]
private void UnComment()
{
if (RoslynEditorViewModel.CodeEditor == null)
{
return;
}
var roslynEditor = RoslynEditorViewModel.CodeEditor;
var document = roslynEditor.Document;
var startLine = document.GetLineByOffset(roslynEditor.SelectionStart);
var endLine = document.GetLineByOffset(roslynEditor.SelectionStart + roslynEditor.SelectionLength);
document.BeginUpdate();
var line = startLine;
while (line != null && line.LineNumber <= endLine.LineNumber)
{
var whitespace = TextUtilities.GetLeadingWhitespace(document, line);
if (line.Length > whitespace.Length + 2)
{
var text = document.GetText(whitespace.EndOffset, 2);
if (text == "//")
document.Remove(whitespace.EndOffset, 2);
}
line = line.NextLine;
}
document.EndUpdate();
}
//public void Receive(string message)
//{
// this.Text = message;
//}
}
}