Browse Source

Scripts now work in a nullable context. Also updated loading of diagnostics to avoid "Unresolved diagnostic" errors, as well as letting the ScriptEditorWindow show warnings

Kenric Nugteren 1 month ago
parent
commit
65d0876715

+ 19 - 5
inabox.scripting/ScriptDocument.cs

@@ -13,6 +13,7 @@ using System.Threading;
 using System.Threading.Tasks;
 using InABox.Core;
 using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
 using Microsoft.CodeAnalysis.CSharp.Scripting;
 using Microsoft.CodeAnalysis.CSharp.Scripting.Hosting;
 using Microsoft.CodeAnalysis.Scripting;
@@ -180,7 +181,7 @@ public class ScriptDocument : INotifyPropertyChanged
         Result = null;
         compiled = null;
 
-        Script = CSharpScript.Create(Text, ScriptOptions.Default
+        Script = CSharpScript.Create($"#nullable enable {Text}", ScriptOptions.Default
             .AddReferences(CompilationReferences)
             .AddImports(Host.DefaultImports));
 
@@ -190,11 +191,24 @@ public class ScriptDocument : INotifyPropertyChanged
         Diagnostics.Clear();
         foreach(var diagnostic in diagnostics)
         {
-            var formatted = FormatObject(diagnostic);
-            if (!formatted.StartsWith("CSDiagnostic(")) continue;
+            var argsProperty = typeof(Microsoft.CodeAnalysis.Diagnostic).GetProperty("Arguments", BindingFlags.Instance | BindingFlags.NonPublic);
+            if(argsProperty is not null)
+            {
+                var value = (argsProperty.GetValue(diagnostic) as IEnumerable<object?>) ?? [];
+                var message = string.Format(diagnostic.Descriptor.MessageFormat.ToString(), value.ToArray());
+                var span = diagnostic.Location.GetMappedLineSpan();
+                Diagnostics.Add(new(diagnostic.Severity, $"({span.StartLinePosition.Line},{span.StartLinePosition.Character}): " +
+                    $"{diagnostic.Severity.ToString().ToLower()} {diagnostic.Id}: " +
+                    $"{message}"));
+            }
+            else
+            {
+                var formatted = FormatObject(diagnostic);
+                if (!formatted.StartsWith("CSDiagnostic(")) continue;
 
-            var content = formatted.Split(new[] { Environment.NewLine }, StringSplitOptions.None).First().Replace("CSDiagnostic(", "").Replace(") {", "");
-            Diagnostics.Add(new(diagnostic.Severity, content));
+                var content = formatted.Split(new[] { Environment.NewLine }, StringSplitOptions.None).First().Replace("CSDiagnostic(", "").Replace(") {", "");
+                Diagnostics.Add(new(diagnostic.Severity, content));
+            }
         }
 
         if (Diagnostics.Any(x => x.Severity == DiagnosticSeverity.Error))

+ 17 - 2
inabox.wpf/DynamicGrid/Controls/ScriptEditor.xaml

@@ -120,8 +120,23 @@
         </Grid>
 
         <GridSplitter Grid.Row="2" Grid.ColumnSpan="4" ResizeDirection="Rows" Height="2" />
-        <ListBox x:Name="Errors" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="4" Margin="0,0,0,0"
-                 SelectionChanged="Errors_SelectionChanged" PreviewMouseDown="Errors_PreviewMouseDown" />
+        <Grid Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="4" Margin="0">
+            <Grid.ColumnDefinitions>
+                <ColumnDefinition Width="*"/>
+                <ColumnDefinition Width="Auto"/>
+            </Grid.ColumnDefinitions>
+            <ListBox x:Name="Errors" Grid.Column="0" Grid.ColumnSpan="2"
+                     SelectionChanged="Errors_SelectionChanged" PreviewMouseDown="Errors_PreviewMouseDown" />
+            <DockPanel x:Name="ErrorButtons" Grid.Column="1" LastChildFill="False" Margin="0,2,2,2">
+                <Button x:Name="ShowErrorsButton" DockPanel.Dock="Top"
+                        Width="25" Height="25"
+                        Click="ShowErrors_Click"/>
+                <Button x:Name="ShowWarningsButton" DockPanel.Dock="Top"
+                        Margin="0,2,0,0"
+                        Width="25" Height="25"
+                        Click="ShowWarnings_Click"/>
+            </DockPanel>
+        </Grid>
         <GridSplitter HorizontalAlignment="Left" Height="100" Margin="-115,494,0,0" Grid.Row="1" Grid.RowSpan="2"
                       VerticalAlignment="Top" Width="5" />
 

+ 71 - 10
inabox.wpf/DynamicGrid/Controls/ScriptEditor.xaml.cs

@@ -64,6 +64,38 @@ namespace InABox.DynamicGrid
         private readonly Dictionary<bool, Image> save = SetupImage(Wpf.Resources.disk);
         private readonly Dictionary<bool, Image> undo = SetupImage(Wpf.Resources.undo);
 
+        private readonly Dictionary<bool, Image> errors = SetupImage(Wpf.Resources.delete);
+        private readonly Dictionary<bool, Image> warnings = SetupImage(Wpf.Resources.warning);
+
+        private bool _showErrors;
+        public bool ShowErrors
+        {
+            get => _showErrors;
+            set
+            {
+                _showErrors = value;
+                UpdateDiagnosticButton(ShowErrorsButton, errors, value);
+                RefreshDiagnostics();
+            }
+        }
+
+        private bool _showWarnings;
+        public bool ShowWarnings
+        {
+            get => _showWarnings;
+            set
+            {
+                _showWarnings = value;
+                UpdateDiagnosticButton(ShowWarningsButton, warnings, value);
+                RefreshDiagnostics();
+            }
+        }
+
+        private void UpdateDiagnosticButton(Button button, Dictionary<bool, Image> image, bool enabled)
+        {
+            button.Content = image[enabled];
+        }
+
         static ScriptEditorWindow()
         {
             SaveCommand.InputGestures.Add(new KeyGesture(Key.S, ModifierKeys.Control));
@@ -127,6 +159,9 @@ namespace InABox.DynamicGrid
             CheckButton(CompileButton, language == SyntaxLanguage.CSharp || language == SyntaxLanguage.XAML, compile);
             CheckButton(RunButton, false, run);
 
+            ShowErrors = true;
+            ShowWarnings = false;
+
             var timer = new DispatcherTimer();
             timer.Interval = TimeSpan.FromMilliseconds(500);
             timer.Tick += Timer_Tick;
@@ -354,6 +389,30 @@ namespace InABox.DynamicGrid
             Close();
         }
 
+        private List<ScriptDocument.Diagnostic> Diagnostics = new();
+
+        private void RefreshDiagnostics()
+        {
+            var diagnostics = Diagnostics.Where(x => x.Severity switch
+            {
+                DiagnosticSeverity.Error => ShowErrors,
+                DiagnosticSeverity.Warning => ShowWarnings,
+                _ => false
+            }).ToList();
+            Errors.Items.Clear();
+            if (bCompiled && diagnostics.Count == 0)
+            {
+                Errors.Items.Add("Script Compiled Successfully!");
+            }
+            else
+            {
+                foreach(var diagnostic in diagnostics)
+                {
+                    Errors.Items.Add(diagnostic.Contents);
+                }
+            }
+        }
+
         private void CompileButton_Click(object sender, RoutedEventArgs e)
         {
             CheckButton(RunButton, false, run);
@@ -372,18 +431,10 @@ namespace InABox.DynamicGrid
                 Task.Run(() =>
                 {
                     bCompiled = viewModel.Compile();
+                    Diagnostics = viewModel.Diagnostics;
                     Dispatcher.Invoke(() =>
                     {
-                        Errors.Items.Clear();
-                        if (bCompiled)
-                        {
-                            Errors.Items.Add("Script Compiled Successfully!");
-                        }
-                        else
-                        {
-                            var errors = viewModel.Result.Split(new[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
-                            errors.ForEach(x => Errors.Items.Add(x));
-                        }
+                        RefreshDiagnostics();
 
                         CheckButton(RunButton, bCompiled, run);
                     });
@@ -511,6 +562,16 @@ namespace InABox.DynamicGrid
             e.CanExecute = _onSave != null;
         }
 
+        private void ShowErrors_Click(object sender, RoutedEventArgs e)
+        {
+            ShowErrors = !ShowErrors;
+        }
+
+        private void ShowWarnings_Click(object sender, RoutedEventArgs e)
+        {
+            ShowWarnings = !ShowWarnings;
+        }
+
         #endregion
     }
 }