Forge
csharpdeeb25a2
1using CascadeIDE.Services;
2using Microsoft.CodeAnalysis.Text;
3using Xunit;
4using TestContext = Xunit.TestContext;
5
6namespace CascadeIDE.Tests.MonacoForward;
7
8[Trait("Category", "MonacoForward")]
9public sealed class CSharpLanguageServiceCompletionTests
10{
11 [Fact]
12 public void MemberAccess_lists_type_members_not_global_scope()
13 {
14 const string path = @"D:\Fake\MemberComplete.cs";
15 var src = """
16 using System;
17 class C
18 {
19 public string Title { get; set; }
20 public int Count;
21 public void Run() { }
22 void M()
23 {
24 var sb = new System.Text.StringBuilder();
25 sb.
26 }
27 }
28 """;
29 var (line, column) = AfterMarker(src, "sb.");
30 var svc = new CSharpLanguageService();
31 var items = svc.GetCompletionItems(path, src, line, column, TestContext.Current.CancellationToken);
32
33 Assert.NotEmpty(items);
34 Assert.Contains(items, i => i.DisplayText == "Append" && i.Kind == CSharpLanguageService.CSharpCompletionKind.Method);
35 Assert.Contains(items, i => i.DisplayText == "Length" && i.Kind == CSharpLanguageService.CSharpCompletionKind.Property);
36 Assert.DoesNotContain(items, i => i.DisplayText == "Title");
37 Assert.DoesNotContain(items, i => i.DisplayText == "while");
38 }
39
40 [Fact]
41 public void MemberAccess_includes_property_and_field_on_own_type()
42 {
43 const string path = @"D:\Fake\OwnMembers.cs";
44 var src = """
45 class Box
46 {
47 public string Label;
48 public int Size { get; set; }
49 void M(Box b) { b. }
50 }
51 """;
52 var (line, column) = AfterMarker(src, "b. ");
53 var svc = new CSharpLanguageService();
54 var items = svc.GetCompletionItems(path, src, line, column, TestContext.Current.CancellationToken);
55
56 Assert.Contains(items, i => i.DisplayText == "Label" && i.Kind == CSharpLanguageService.CSharpCompletionKind.Field);
57 Assert.Contains(items, i => i.DisplayText == "Size" && i.Kind == CSharpLanguageService.CSharpCompletionKind.Property);
58 }
59
60 [Fact]
61 public void ScopeCompletion_uses_names_not_entire_symbol_dump()
62 {
63 const string path = @"D:\Fake\Scope.cs";
64 var src = """
65 class C
66 {
67 void Outer()
68 {
69 int local = 1;
70 loc
71 }
72 }
73 """;
74 var (line, column) = AfterMarker(src, "loc");
75 var svc = new CSharpLanguageService();
76 var items = svc.GetCompletionItems(path, src, line, column, TestContext.Current.CancellationToken);
77
78 Assert.Contains(items, i => i.DisplayText == "local");
79 Assert.DoesNotContain(items, i => i.DisplayText == "Outer" && i.Kind == CSharpLanguageService.CSharpCompletionKind.Method);
80 }
81
82 [Fact]
83 public void ScopeCompletion_acronym_matches_types_after_new_when_text_imported()
84 {
85 const string path = @"D:\Fake\Acronym.cs";
86 var src = """
87 using System.Text;
88 class C { void M() { var x = new SB } }
89 """;
90 var (line, column) = AfterMarker(src, "SB");
91 var svc = new CSharpLanguageService();
92 var items = svc.GetCompletionItems(path, src, line, column, TestContext.Current.CancellationToken);
93
94 Assert.Contains(items, i => i.DisplayText == "StringBuilder");
95 Assert.Contains(items, i => i.DisplayText == "SByte");
96 }
97
98 [Fact]
99 public void ScopeCompletion_acronym_matches_types_in_project_with_global_usings()
100 {
101 var csproj = FindRepoFile(@"CasaField.Core\CasaField.Core.csproj");
102 var path = Path.Combine(Path.GetDirectoryName(csproj)!, "AcronymSample.cs");
103 var src = "class C { void M() { var x = new SB } }";
104 var (line, column) = AfterMarker(src, "SB");
105 var svc = new CSharpLanguageService();
106 var items = svc.GetCompletionItems(path, src, line, column, TestContext.Current.CancellationToken);
107
108 Assert.Contains(items, i => i.DisplayText == "SByte");
109 }
110
111 [Fact]
112 public void NamespaceDeclaration_lists_child_namespaces_not_statement_keywords()
113 {
114 const string path = @"D:\Fake\NsDecl.cs";
115 var src = """
116 namespace Casa.
117 class C { }
118 """;
119 var (line, column) = AfterMarker(src, "namespace Casa.");
120 var svc = new CSharpLanguageService();
121 var items = svc.GetCompletionItems(path, src, line, column, TestContext.Current.CancellationToken);
122
123 Assert.DoesNotContain(items, i => i.DisplayText == "while");
124 Assert.DoesNotContain(items, i => i.DisplayText == "class");
125 Assert.DoesNotContain(items, i => i.DisplayText == "namespace");
126 }
127
128 [Fact]
129 public void FileScopedNamespaceDeclaration_filters_prefix_without_keywords()
130 {
131 const string path = @"D:\Fake\FsNs.cs";
132 var src = "namespace Cas";
133 var (line, column) = AfterMarker(src, "Cas");
134 var svc = new CSharpLanguageService();
135 var items = svc.GetCompletionItems(path, src, line, column, TestContext.Current.CancellationToken);
136
137 Assert.DoesNotContain(items, i => i.DisplayText == "while");
138 Assert.DoesNotContain(items, i => i.DisplayText == "catch");
139 }
140
141 [Fact]
142 public void ScopeCompletion_acronym_matches_types()
143 {
144 const string path = @"D:\Fake\Acronym.cs";
145 var src = """
146 using System.Text;
147 class C { void M() { SB } }
148 """;
149 var (line, column) = AfterMarker(src, "SB");
150 var svc = new CSharpLanguageService();
151 var items = svc.GetCompletionItems(path, src, line, column, TestContext.Current.CancellationToken);
152
153 Assert.Contains(items, i => i.DisplayText == "StringBuilder");
154 Assert.Contains(items, i => i.DisplayText == "SByte");
155 }
156
157 [Fact]
158 public void ScopeCompletion_includes_compiler_keywords_from_syntax_facts()
159 {
160 const string path = @"D:\Fake\Keywords.cs";
161 var src = """
162 class C
163 {
164 void M()
165 {
166 rec
167 }
168 }
169 """;
170 var (line, column) = AfterMarker(src, "rec");
171 var svc = new CSharpLanguageService();
172 var items = svc.GetCompletionItems(path, src, line, column, TestContext.Current.CancellationToken);
173
174 Assert.Contains(items, i => i.DisplayText == "record" && i.Kind == CSharpLanguageService.CSharpCompletionKind.Keyword);
175 }
176
177 private static (int Line, int Column) AfterMarker(string source, string marker)
178 {
179 var index = source.IndexOf(marker, StringComparison.Ordinal);
180 Assert.True(index >= 0, $"Marker '{marker}' not found.");
181 var position = index + marker.Length;
182 var location = SourceText.From(source).Lines.GetLinePosition(position);
183 return (location.Line + 1, location.Character + 1);
184 }
185
186 private static string FindRepoFile(string relativePath)
187 {
188 var dir = AppContext.BaseDirectory;
189 for (var i = 0; i < 8; i++)
190 {
191 var candidate = Path.GetFullPath(Path.Combine(dir, relativePath));
192 if (File.Exists(candidate))
193 return candidate;
194 dir = Path.GetDirectoryName(dir)!;
195 }
196
197 throw new FileNotFoundException(relativePath);
198 }
199}
200
View only · write via MCP/CIDE