ScriptModel.cs 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Runtime.CompilerServices;
  7. using System.Threading.Tasks;
  8. using Microsoft.CodeAnalysis;
  9. using Microsoft.CodeAnalysis.CSharp.Scripting;
  10. using Microsoft.CodeAnalysis.CSharp.Scripting.Hosting;
  11. using Microsoft.CodeAnalysis.Scripting;
  12. using Microsoft.CodeAnalysis.Scripting.Hosting;
  13. using RoslynPad.Roslyn;
  14. namespace InABox.DynamicGrid
  15. {
  16. internal class DocumentViewModel : INotifyPropertyChanged
  17. {
  18. private readonly RoslynHost _host;
  19. private bool _isReadOnly;
  20. private string _result;
  21. public DocumentViewModel(RoslynHost host, DocumentViewModel previous)
  22. {
  23. _host = host;
  24. Previous = previous;
  25. }
  26. public DocumentId Id { get; private set; }
  27. public bool IsReadOnly
  28. {
  29. get => _isReadOnly;
  30. private set => SetProperty(ref _isReadOnly, value);
  31. }
  32. public DocumentViewModel Previous { get; }
  33. public DocumentViewModel LastGoodPrevious
  34. {
  35. get
  36. {
  37. var previous = Previous;
  38. while (previous != null && previous.HasError) previous = previous.Previous;
  39. return previous;
  40. }
  41. }
  42. public Script<object> Script { get; private set; }
  43. public string Text { get; set; }
  44. public bool HasError { get; private set; }
  45. public string Result
  46. {
  47. get => _result;
  48. private set => SetProperty(ref _result, value);
  49. }
  50. private static MethodInfo HasSubmissionResult { get; } =
  51. typeof(Compilation).GetMethod(nameof(HasSubmissionResult), BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
  52. private static PrintOptions PrintOptions { get; } = new() { MemberDisplayFormat = MemberDisplayFormat.SeparateLines };
  53. public event PropertyChangedEventHandler PropertyChanged;
  54. internal void Initialize(DocumentId id)
  55. {
  56. Id = id;
  57. }
  58. public async Task<bool> TrySubmit()
  59. {
  60. Result = null;
  61. Script = LastGoodPrevious?.Script.ContinueWith(Text) ??
  62. CSharpScript.Create(Text, ScriptOptions.Default
  63. .WithReferences(_host.DefaultReferences)
  64. .WithImports(_host.DefaultImports));
  65. var compilation = Script.GetCompilation();
  66. var hasResult = (bool)HasSubmissionResult.Invoke(compilation, null);
  67. var diagnostics = Script.Compile();
  68. if (diagnostics.Any(t => t.Severity == DiagnosticSeverity.Error))
  69. {
  70. Result = string.Join(Environment.NewLine, diagnostics.Select(FormatObject));
  71. return false;
  72. }
  73. IsReadOnly = true;
  74. await Execute(hasResult);
  75. return true;
  76. }
  77. private async Task Execute(bool hasResult)
  78. {
  79. try
  80. {
  81. var result = await Script.RunAsync();
  82. if (result.Exception != null)
  83. {
  84. HasError = true;
  85. Result = FormatException(result.Exception);
  86. }
  87. else
  88. {
  89. Result = hasResult ? FormatObject(result.ReturnValue) : null;
  90. }
  91. }
  92. catch (Exception ex)
  93. {
  94. HasError = true;
  95. Result = FormatException(ex);
  96. }
  97. }
  98. private static string FormatException(Exception ex)
  99. {
  100. return CSharpObjectFormatter.Instance.FormatException(ex);
  101. }
  102. private static string FormatObject(object o)
  103. {
  104. return CSharpObjectFormatter.Instance.FormatObject(o, PrintOptions);
  105. }
  106. protected bool SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
  107. {
  108. if (!EqualityComparer<T>.Default.Equals(field, value))
  109. {
  110. field = value;
  111. // ReSharper disable once ExplicitCallerInfoArgument
  112. OnPropertyChanged(propertyName);
  113. return true;
  114. }
  115. return false;
  116. }
  117. protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
  118. {
  119. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  120. }
  121. }
  122. }