| 1 | using System.Linq; |
| 2 | using CascadeIDE.Services; |
| 3 | using Xunit; |
| 4 | using TestContext = Xunit.TestContext; |
| 5 | |
| 6 | namespace CascadeIDE.Tests; |
| 7 | |
| 8 | public class CSharpLanguageServiceInlayTests |
| 9 | { |
| 10 | [Fact] |
| 11 | public void GetVarInlayHints_ForVarLocal_ReturnsInferredTypeLabel() |
| 12 | { |
| 13 | const string path = @"D:\Fake\InlayVar.cs"; |
| 14 | var src = """ |
| 15 | class C |
| 16 | { |
| 17 | void M() |
| 18 | { |
| 19 | var x = 1; |
| 20 | } |
| 21 | } |
| 22 | """; |
| 23 | var svc = new CSharpLanguageService(); |
| 24 | var parts = svc.GetVarInlayHintsForFile(path, src, TestContext.Current.CancellationToken); |
| 25 | var h = Assert.Single(parts); |
| 26 | Assert.Contains("int", h.Label, StringComparison.Ordinal); |
| 27 | var i = src.IndexOf("var", StringComparison.Ordinal); |
| 28 | Assert.True(i >= 0); |
| 29 | Assert.Equal(i + 3, h.AnchorOffset); |
| 30 | Assert.Equal(' ', src[h.AnchorOffset]); |
| 31 | } |
| 32 | |
| 33 | [Fact] |
| 34 | public void GetVarInlayHints_ForEachVar_ReturnsElementType() |
| 35 | { |
| 36 | const string path = @"D:\Fake\InlayForeach.cs"; |
| 37 | const string src = "class C { void M() { foreach (var x in new int[0]) { } } }"; |
| 38 | var svc = new CSharpLanguageService(); |
| 39 | var parts = svc.GetVarInlayHintsForFile(path, src, TestContext.Current.CancellationToken); |
| 40 | var h = Assert.Single(parts); |
| 41 | Assert.Contains("int", h.Label, StringComparison.Ordinal); |
| 42 | var j = src.IndexOf("var", StringComparison.Ordinal); |
| 43 | Assert.Equal(j + 3, h.AnchorOffset); |
| 44 | } |
| 45 | |
| 46 | [Fact] |
| 47 | public void GetVarInlayHints_NonCs_ReturnsEmpty() |
| 48 | { |
| 49 | var svc = new CSharpLanguageService(); |
| 50 | Assert.Empty(svc.GetVarInlayHintsForFile(@"D:\a.txt", "x", TestContext.Current.CancellationToken)); |
| 51 | } |
| 52 | |
| 53 | [Fact] |
| 54 | public void GetVarInlayHints_Invocation_SkipsInlayWhenIdentifierEqualsParamName() |
| 55 | { |
| 56 | const string path = @"D:\Fake\InlayParamSameName.cs"; |
| 57 | const string src = """ |
| 58 | static void F(string[] args) { } |
| 59 | static void M(string[] args) { F(args); } |
| 60 | """; |
| 61 | var svc = new CSharpLanguageService(); |
| 62 | var parts = svc.GetVarInlayHintsForFile(path, src, TestContext.Current.CancellationToken); |
| 63 | Assert.DoesNotContain(parts, p => p.Label.Contains("args", StringComparison.Ordinal) && p.Label.Contains(':')); |
| 64 | } |
| 65 | |
| 66 | [Fact] |
| 67 | public void GetVarInlayHints_ParamsArray_EmitsOneLabelForParamsParameter() |
| 68 | { |
| 69 | const string path = @"D:\Fake\InlayParams.cs"; |
| 70 | const string src = "class C { static void M(params int[] items) { } void K() { M(1, 2, 3); } }"; |
| 71 | var svc = new CSharpLanguageService(); |
| 72 | var parts = svc.GetVarInlayHintsForFile(path, src, TestContext.Current.CancellationToken); |
| 73 | Assert.Single(parts, p => p.Label.Contains("items", StringComparison.Ordinal) && p.Label.Contains(':')); |
| 74 | int call = src.IndexOf("M(1, 2, 3)", StringComparison.Ordinal); |
| 75 | int one = call + 2; |
| 76 | Assert.Equal(one, parts.First(p => p.Label.Contains("items", StringComparison.Ordinal)).AnchorOffset); |
| 77 | } |
| 78 | |
| 79 | [Fact] |
| 80 | public void GetVarInlayHints_Invocation_InsertsParameterNameLabels() |
| 81 | { |
| 82 | const string path = @"D:\Fake\InlayParamInvoke.cs"; |
| 83 | const string src = """ |
| 84 | class C |
| 85 | { |
| 86 | void T(int alpha, int beta) { } |
| 87 | void M() { T(1, 2); } |
| 88 | } |
| 89 | """; |
| 90 | var svc = new CSharpLanguageService(); |
| 91 | var parts = svc.GetVarInlayHintsForFile(path, src, TestContext.Current.CancellationToken); |
| 92 | Assert.Contains(parts, p => p.Label.Contains("alpha", StringComparison.Ordinal) && p.Label.Contains(':')); |
| 93 | Assert.Contains(parts, p => p.Label.Contains("beta", StringComparison.Ordinal) && p.Label.Contains(':')); |
| 94 | int call = src.IndexOf("T(1, 2)", StringComparison.Ordinal); |
| 95 | int one = call + 2; |
| 96 | int two = call + 5; // "T(1, 2)" → '2' после ", " |
| 97 | Assert.Equal(one, parts.First(p => p.Label.Contains("alpha", StringComparison.Ordinal)).AnchorOffset); |
| 98 | Assert.Equal(two, parts.First(p => p.Label.Contains("beta", StringComparison.Ordinal)).AnchorOffset); |
| 99 | } |
| 100 | |
| 101 | [Fact] |
| 102 | public void GetVarInlayHints_NamedArguments_SkipsInlays() |
| 103 | { |
| 104 | const string path = @"D:\Fake\InlayNamed.cs"; |
| 105 | const string src = "class C { void T(int a, int b) { } void M() { T(a: 1, b: 2); } }"; |
| 106 | var svc = new CSharpLanguageService(); |
| 107 | var parts = svc.GetVarInlayHintsForFile(path, src, TestContext.Current.CancellationToken); |
| 108 | Assert.Empty(parts); |
| 109 | } |
| 110 | |
| 111 | [Fact] |
| 112 | public void GetVarInlayHints_ObjectCreation_InsertsParameterNameLabel() |
| 113 | { |
| 114 | const string path = @"D:\Fake\InlayNew.cs"; |
| 115 | const string src = "class C { C(int kappa) { } void M() { new C(9); } }"; |
| 116 | var svc = new CSharpLanguageService(); |
| 117 | var parts = svc.GetVarInlayHintsForFile(path, src, TestContext.Current.CancellationToken); |
| 118 | var h = Assert.Single(parts, p => p.Label.Contains("kappa", StringComparison.Ordinal)); |
| 119 | int idx = src.IndexOf("9", StringComparison.Ordinal); |
| 120 | Assert.Equal(idx, h.AnchorOffset); |
| 121 | } |
| 122 | |
| 123 | [Fact] |
| 124 | public void GetVarInlayHints_BclObjectCreationStringArg_InsertsMessageLabel() |
| 125 | { |
| 126 | const string path = @"D:\Fake\InlayBcl.cs"; |
| 127 | const string src = "class C { void M() { new System.IO.FileNotFoundException(\"x\"); } }"; |
| 128 | var svc = new CSharpLanguageService(); |
| 129 | var parts = svc.GetVarInlayHintsForFile(path, src, TestContext.Current.CancellationToken); |
| 130 | Assert.Contains(parts, p => p.Label.Contains("message", StringComparison.Ordinal) && p.Label.Contains(':')); |
| 131 | } |
| 132 | |
| 133 | [Fact] |
| 134 | public void GetVarInlayHints_InvalidOperationException_InsertsMessageLabel() |
| 135 | { |
| 136 | const string path = @"D:\Fake\InlayInvalidOperation.cs"; |
| 137 | const string src = "class C { void M() { throw new InvalidOperationException(\"x\"); } }"; |
| 138 | var svc = new CSharpLanguageService(); |
| 139 | var parts = svc.GetVarInlayHintsForFile(path, src, TestContext.Current.CancellationToken); |
| 140 | Assert.Contains(parts, p => p.Label.Contains("message", StringComparison.Ordinal) && p.Label.Contains(':')); |
| 141 | } |
| 142 | |
| 143 | [Fact] |
| 144 | public void GetVarInlayHints_TargetTypedNew_InsertsMessageLabel() |
| 145 | { |
| 146 | const string path = @"D:\Fake\InlayTargetTypedNew.cs"; |
| 147 | const string src = "class C { void M() { System.Exception ex = new(\"x\"); } }"; |
| 148 | var svc = new CSharpLanguageService(); |
| 149 | var parts = svc.GetVarInlayHintsForFile(path, src, TestContext.Current.CancellationToken); |
| 150 | Assert.Contains(parts, p => p.Label.Contains("message", StringComparison.Ordinal) && p.Label.Contains(':')); |
| 151 | } |
| 152 | |
| 153 | [Fact] |
| 154 | public void GetVarInlayHints_Indexer_InsertsParameterNameLabels() |
| 155 | { |
| 156 | const string path = @"D:\Fake\InlayIndex.cs"; |
| 157 | const string src = "class C { int this[int iota, int kappa] => 0; int M() => this[3, 4]; }"; |
| 158 | var svc = new CSharpLanguageService(); |
| 159 | var parts = svc.GetVarInlayHintsForFile(path, src, TestContext.Current.CancellationToken); |
| 160 | Assert.Contains(parts, p => p.Label.Contains("iota", StringComparison.Ordinal)); |
| 161 | Assert.Contains(parts, p => p.Label.Contains("kappa", StringComparison.Ordinal)); |
| 162 | } |
| 163 | } |
| 164 | |