-
Notifications
You must be signed in to change notification settings - Fork 12
Add initial support for structured diffs #127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -261,7 +261,7 @@ .. GenerateFollowerCachingChanges(oldState, newState, db => db.MaterializedViews | |||||||||||||||||||||
|
|
||||||||||||||||||||||
| foreach(var script in result.SelectMany(itm => itm.Scripts)) | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| var code = KustoCode.Parse(script.Text); | ||||||||||||||||||||||
| var code = KustoCode.Parse(script.Script.Text); | ||||||||||||||||||||||
| var diagnostics = code.GetDiagnostics(); | ||||||||||||||||||||||
| script.IsValid = diagnostics.Any() == false; | ||||||||||||||||||||||
|
||||||||||||||||||||||
| script.IsValid = diagnostics.Any() == false; | |
| script.IsValid = diagnostics.Any() == false; | |
| script.Diagnostics = diagnostics.Any() | |
| ? diagnostics.Select(diagnostic => new ScriptDiagnostic | |
| { | |
| Start = diagnostic.Start, | |
| End = diagnostic.End, | |
| Description = diagnostic.Description | |
| }).ToList() | |
| : null; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -22,7 +22,7 @@ public List<DatabaseScriptContainer> Scripts | |||||||||||||||||||||
| get | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| var sc = new DatabaseScriptContainer("Deletion", 0, $".drop {EntityType} {Entity}"); | ||||||||||||||||||||||
| var code = KustoCode.Parse(sc.Text); | ||||||||||||||||||||||
| var code = KustoCode.Parse(sc.Script.Text); | ||||||||||||||||||||||
| var diagnostics = code.GetDiagnostics(); | ||||||||||||||||||||||
| sc.IsValid = diagnostics.Any() == false; | ||||||||||||||||||||||
|
||||||||||||||||||||||
| sc.IsValid = diagnostics.Any() == false; | |
| sc.IsValid = diagnostics.Any() == false; | |
| sc.Diagnostics = diagnostics.Any() | |
| ? diagnostics.Select(diagnostic => new ScriptDiagnostic | |
| { | |
| Start = diagnostic.Start, | |
| End = diagnostic.End, | |
| Description = diagnostic.Description | |
| }).ToList() | |
| : null; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -34,7 +34,7 @@ private void Init() | |||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| Scripts.Add(toScript); | ||||||||||||||||||||||
| var code = KustoCode.Parse(toScript.Text); | ||||||||||||||||||||||
| var code = KustoCode.Parse(toScript.Script.Text); | ||||||||||||||||||||||
| var diagnostics = code.GetDiagnostics(); | ||||||||||||||||||||||
|
||||||||||||||||||||||
| var diagnostics = code.GetDiagnostics(); | |
| var diagnostics = code.GetDiagnostics(); | |
| toScript.Diagnostics = diagnostics.Any() | |
| ? diagnostics.Select(diagnostic => new ScriptDiagnostic | |
| { | |
| Start = diagnostic.Start, | |
| End = diagnostic.End, | |
| Description = diagnostic.Description | |
| }).ToList() | |
| : null; |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -7,12 +7,13 @@ | |||||
| using System.Data; | ||||||
| using DiffPlex.DiffBuilder.Model; | ||||||
| using Kusto.Language.Editor; | ||||||
| using System.Linq; | ||||||
|
|
||||||
| namespace KustoSchemaTools.Changes | ||||||
| { | ||||||
| public class ScriptCompareChange : BaseChange<IKustoBaseEntity> | ||||||
| { | ||||||
| public ScriptCompareChange(string entity, IKustoBaseEntity? from, IKustoBaseEntity to) : base(to.GetType().Name, entity, from, to) | ||||||
|
Check warning on line 16 in KustoSchemaTools/Changes/ScriptCompareChange.cs
|
||||||
| { | ||||||
| Init(); | ||||||
| } | ||||||
|
|
@@ -32,8 +33,8 @@ | |||||
| foreach (var change in to) | ||||||
| { | ||||||
| var before = from.ContainsKey(change.Kind) ? from[change.Kind] : null; | ||||||
| var beforeText = before?.Text ?? ""; | ||||||
| var afterText = change.Text; | ||||||
| var beforeText = before?.Script.Text ?? string.Empty; | ||||||
| var afterText = change.Script.Text; | ||||||
|
|
||||||
| var singleLinebeforeText = new KustoCodeService(KustoCode.Parse(beforeText)).GetMinimalText(MinimalTextKind.SingleLine); | ||||||
| var singleLineafterText = new KustoCodeService(KustoCode.Parse(afterText)).GetMinimalText(MinimalTextKind.SingleLine); | ||||||
|
|
@@ -51,10 +52,19 @@ | |||||
| var diff = InlineDiffBuilder.Diff(beforeText, afterText, true); | ||||||
| if (diff.Lines.All(itm => itm.Type == ChangeType.Unchanged)) continue; | ||||||
|
|
||||||
| var code = KustoCode.Parse(change.Text); | ||||||
| var code = KustoCode.Parse(change.Script.Text); | ||||||
|
|
||||||
| var diagnostics = code.GetDiagnostics(); | ||||||
| change.IsValid = diagnostics.Any() == false || change.Order == -1; | ||||||
| var hasDiagnostics = diagnostics.Any(); | ||||||
| change.IsValid = hasDiagnostics == false || change.Script.Order == -1; | ||||||
|
||||||
| change.IsValid = hasDiagnostics == false || change.Script.Order == -1; | |
| change.IsValid = !hasDiagnostics || change.Script.Order == -1; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,225 @@ | ||||||||||||||||||||||||||||
| using System; | ||||||||||||||||||||||||||||
| using System.Collections.Generic; | ||||||||||||||||||||||||||||
| using System.Linq; | ||||||||||||||||||||||||||||
| using System.Text; | ||||||||||||||||||||||||||||
| using DiffPlex; | ||||||||||||||||||||||||||||
| using DiffPlex.DiffBuilder; | ||||||||||||||||||||||||||||
| using DiffPlex.DiffBuilder.Model; | ||||||||||||||||||||||||||||
| using KustoSchemaTools.Model; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| namespace KustoSchemaTools.Changes | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| public static class StructuredChangeExtensions | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| public static StructuredChange ToStructuredChange(this IChange change) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| if (change == null) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| throw new ArgumentNullException(nameof(change)); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| var structuredChange = new StructuredChange | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| EntityType = change.EntityType, | ||||||||||||||||||||||||||||
| Entity = change.Entity, | ||||||||||||||||||||||||||||
| Scripts = change.Scripts?.Select(CloneScript).ToList() ?? new List<DatabaseScriptContainer>(), | ||||||||||||||||||||||||||||
| Comment = StructuredComment.From(change.Comment) | ||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| switch (change) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| case Heading heading: | ||||||||||||||||||||||||||||
| structuredChange.ChangeType = "Heading"; | ||||||||||||||||||||||||||||
| structuredChange.HeadingText = heading.Entity; | ||||||||||||||||||||||||||||
| structuredChange.Scripts.Clear(); | ||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||
| case DeletionChange deletion: | ||||||||||||||||||||||||||||
| structuredChange.ChangeType = "Delete"; | ||||||||||||||||||||||||||||
| structuredChange.DeletedEntities = new List<string> { deletion.Entity }; | ||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||
| case ScriptCompareChange scriptCompare: | ||||||||||||||||||||||||||||
| structuredChange.ChangeType = scriptCompare.From == null ? "Create" : "Update"; | ||||||||||||||||||||||||||||
| structuredChange.ScriptComparison = scriptCompare.ToStructuredScriptComparison(); | ||||||||||||||||||||||||||||
| structuredChange.DiffMarkdown = BuildDiffMarkdown(scriptCompare); | ||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||
| structuredChange.ChangeType = "Update"; | ||||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| structuredChange.DeletedEntities ??= new List<string>(); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| return structuredChange; | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| private static StructuredScriptComparison? ToStructuredScriptComparison(this ScriptCompareChange change) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| var comparison = new StructuredScriptComparison | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| NewScripts = change.Scripts?.Select(CloneScript).ToList() ?? new List<DatabaseScriptContainer>() | ||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| if (change.From != null) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| var previousScripts = change.From | ||||||||||||||||||||||||||||
| .CreateScripts(change.Entity, false) | ||||||||||||||||||||||||||||
| .GroupBy(script => script.Kind) | ||||||||||||||||||||||||||||
| .Select(group => group.First()) | ||||||||||||||||||||||||||||
| .ToDictionary(script => script.Kind, script => script); | ||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||
| foreach (var script in comparison.NewScripts) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| if (previousScripts.TryGetValue(script.Kind, out var previous)) | ||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||
| comparison.OldScripts.Add(CloneScript(previous)); | ||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||
|
Comment on lines
+63
to
+68
|
||||||||||||||||||||||||||||
| foreach (var script in comparison.NewScripts) | |
| { | |
| if (previousScripts.TryGetValue(script.Kind, out var previous)) | |
| { | |
| comparison.OldScripts.Add(CloneScript(previous)); | |
| } | |
| foreach (var script in comparison.NewScripts.Where(s => previousScripts.ContainsKey(s.Kind))) | |
| { | |
| var previous = previousScripts[script.Kind]; | |
| comparison.OldScripts.Add(CloneScript(previous)); |
Check notice
Code scanning / CodeQL
Missed opportunity to use Where Note
implicitly filters its target sequence
Fixed
Show fixed
Hide fixed
Outdated
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inefficient use of 'ContainsKey' and indexer.
| var oldScript = previousScripts.ContainsKey(script.Kind) ? previousScripts[script.Kind] : null; | |
| previousScripts.TryGetValue(script.Kind, out var oldScript); |
Check warning
Code scanning / CodeQL
Useless assignment to local variable Warning
differ
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 5 days ago
To fix the issue, simply remove the assignment and local declaration for the unused variable differ. In BuildDiffPreview, eliminate the line var differ = new Differ(); entirely. No other changes are required; the InlineDiffBuilder.Diff invocation works independently and does not depend on Differ. This change should be limited to the relevant lines in the method body and requires no additional imports or method definitions.
| @@ -132,7 +132,6 @@ | ||
| return new List<string>(); | ||
| } | ||
|
|
||
| var differ = new Differ(); | ||
| var diff = InlineDiffBuilder.Diff(before, after, false); | ||
|
|
||
| var preview = diff.Lines |
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The differ variable is instantiated but never used. The InlineDiffBuilder.Diff method on line 143 doesn't require this variable. Consider removing:
var differ = new Differ();| var differ = new Differ(); |
Check warning
Code scanning / CodeQL
Useless assignment to local variable Warning
differ
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 5 days ago
To fix the problem, simply remove the unnecessary assignment to the differ variable in the BuildDiffMarkdown method. Specifically, delete line 168 (var differ = new Differ();). No additional imports or logic changes are required, as the Differ instance is not used elsewhere in the method and its removal has no impact on program correctness.
| @@ -165,7 +165,6 @@ | ||
| var previousScripts = BuildPreviousScripts(change.From, change.Entity); | ||
|
|
||
| var sb = new StringBuilder(); | ||
| var differ = new Differ(); | ||
| foreach (var script in change.Scripts) | ||
| { | ||
| var before = previousScripts.TryGetValue(script.Kind, out var prior) |
Copilot
AI
Dec 5, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The differ variable is instantiated but never used. The InlineDiffBuilder.Diff method on line 193 doesn't require this variable. Consider removing:
var differ = new Differ();| var differ = new Differ(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Diagnostics should be captured in the
Diagnosticsproperty for consistency withScriptCompareChange. Currently, diagnostics are only used to setIsValidbut not stored. Consider adding: