Skip to content

Commit 4788446

Browse files
slang25idg2claude
authored
feat: add .slnx solution format support (#112)
## Summary Add support for the XML-based `.slnx` solution format introduced in .NET 9 / Visual Studio 17.10. - **Upgrade Roslyn from 4.4.0 to 5.3.0** — `MSBuildWorkspace.OpenSolutionAsync()` gained native `.slnx` support in Roslyn 5.0.0+ ([dotnet/roslyn#77326](dotnet/roslyn#77326)) - **Add `.slnx` to auto-discovery** — `FindSolutionOrProjectFile` now finds `.slnx` files alongside `.sln` - **Add `.slnx` to restore and loading** — solution restore and `OpenSolutionAsync` paths treat `.slnx` same as `.sln` - **Drop `net6.0`/`net7.0` TFMs** — Roslyn 5.x requires `net8.0+`; both .NET 6 and 7 are end-of-life - **Update CI workflow** — drop net6.0/net7.0 from `.github/workflows/test.yml` to match the supported TFMs (this is the additional change requested in the review on #110) - **Add snapshot test** — new `syntax-slnx` test fixture validates SCIP indexing via `.slnx` - **Bump version to 0.2.14** ## Motivation The `.slnx` format is the default for `dotnet new sln` in .NET 10 and is increasingly adopted in modern .NET projects. Without this change, `scip-dotnet` cannot index codebases that use `.slnx`, requiring manual workarounds (passing individual `.csproj` files instead). ## Credit The substantive work here is by @idg2 (original PR #110) — authorship of that commit is preserved. This PR adds the CI workflow update requested by @jupblb in review. ## Test plan - [x] Existing `syntax` snapshot test passes (`.sln` path unchanged) - [x] New `syntax-slnx` snapshot test passes (`.slnx` auto-discovered and indexed) - [x] `dotnet build` succeeds on net8.0/net9.0/net10.0 - [x] `.github/workflows/test.yml` updated to match the dropped TFMs 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Igor Daniel <172804970+idg2@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3e1f671 commit 4788446

93 files changed

Lines changed: 151 additions & 9827 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/test.yml

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@ jobs:
1515
- uses: actions/setup-dotnet@v3
1616
with:
1717
dotnet-version: |
18-
7.x
1918
8.x
2019
9.x
2120
10.x
2221
- run: dotnet format --verify-no-changes
2322
- run: dotnet format --verify-no-changes
2423
working-directory: snapshots/input/syntax
24+
- run: dotnet format --verify-no-changes
25+
working-directory: snapshots/input/syntax-slnx
2526
test:
2627
runs-on: ${{ matrix.os }}
2728
strategy:
@@ -33,13 +34,9 @@ jobs:
3334
- uses: actions/setup-dotnet@v3
3435
with:
3536
dotnet-version: |
36-
6.x
37-
7.x
3837
8.x
3938
9.x
4039
10.x
4140
- run: dotnet test -p:TargetFrameworks=net10.0
4241
- run: dotnet test -p:TargetFrameworks=net9.0
4342
- run: dotnet test -p:TargetFrameworks=net8.0
44-
- run: dotnet test -p:TargetFrameworks=net7.0
45-
- run: dotnet test -p:TargetFrameworks=net6.0

ScipDotnet.Tests/ScipDotnet.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>net10.0;net9.0;net8.0;net7.0;net6.0</TargetFrameworks>
4+
<TargetFrameworks>net10.0;net9.0;net8.0</TargetFrameworks>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
77
<IsPackable>false</IsPackable>

ScipDotnet/IndexCommandHandler.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,14 @@ private static async Task ScipIndex(IHost host, IndexCommandOptions options)
116116
}
117117

118118
private static string FixThisProblem(string examplePath) =>
119-
"To fix this problem, pass the path of a solution (.sln) or project (.csproj/.vbrpoj) file to the `scip-dotnet index` command. " +
119+
"To fix this problem, pass the path of a solution (.sln/.slnx) or project (.csproj/.vbproj) file to the `scip-dotnet index` command. " +
120120
$"For example, run: scip-dotnet index {examplePath}";
121121

122122
private static List<FileInfo> FindSolutionOrProjectFile(FileInfo workingDirectory, ILogger logger)
123123
{
124124
var paths = Directory.GetFiles(workingDirectory.FullName).Where(file =>
125125
string.Equals(Path.GetExtension(file), ".sln", StringComparison.OrdinalIgnoreCase) ||
126+
string.Equals(Path.GetExtension(file), ".slnx", StringComparison.OrdinalIgnoreCase) ||
126127
string.Equals(Path.GetExtension(file), ".csproj", StringComparison.OrdinalIgnoreCase) ||
127128
string.Equals(Path.GetExtension(file), ".vbproj", StringComparison.OrdinalIgnoreCase)
128129
).ToList();
@@ -133,7 +134,7 @@ private static List<FileInfo> FindSolutionOrProjectFile(FileInfo workingDirector
133134
}
134135

135136
logger.LogError(
136-
"No solution (.sln) or .csproj/.vbproj file detected in the working directory '{WorkingDirectory}'. {FixThis}",
137+
"No solution (.sln/.slnx) or .csproj/.vbproj file detected in the working directory '{WorkingDirectory}'. {FixThis}",
137138
workingDirectory.FullName, FixThisProblem("SOLUTION_FILE"));
138139
return new List<FileInfo>();
139140
}

ScipDotnet/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ public static async Task<int> Main(string[] args)
2121
{
2222
var indexCommand = new Command("index", "Index a solution file")
2323
{
24-
new Argument<FileInfo>("projects", "Path to the .sln (solution) or .csproj/.vbproj file")
24+
new Argument<FileInfo>("projects", "Path to the .sln/.slnx (solution) or .csproj/.vbproj file")
2525
{ Arity = ArgumentArity.ZeroOrMore },
2626
new Option<string>("--output", () => "index.scip",
2727
"Path to the output SCIP index file"),

ScipDotnet/ScipDotnet.csproj

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
<OutputType>Exe</OutputType>
55
<ImplicitUsings>enable</ImplicitUsings>
66
<Nullable>enable</Nullable>
7-
<Version>0.2.14</Version>
7+
<Version>0.2.15</Version>
88
<PackageLicenseFile>LICENSE</PackageLicenseFile>
99
<PackageReadmeFile>readme.md</PackageReadmeFile>
1010
<PackageType>DotnetTool</PackageType>
11-
<TargetFrameworks>net10.0;net9.0;net8.0;net7.0;net6.0</TargetFrameworks>
11+
<TargetFrameworks>net10.0;net9.0;net8.0</TargetFrameworks>
1212
<PackAsTool>true</PackAsTool>
1313
<AssemblyName>scip-dotnet</AssemblyName>
1414
<RepositoryUrl>https://github.com/sourcegraph/scip-dotnet</RepositoryUrl>
@@ -19,14 +19,14 @@
1919
<None Include="../readme.md" Pack="true" PackagePath="" />
2020
<PackageReference Include="Google.Protobuf" Version="3.30.0" />
2121
<PackageReference Include="Microsoft.Build.Locator" Version="1.7.8" />
22-
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.4.0" />
23-
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.4.0" />
24-
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.4.0" />
25-
<PackageReference Include="Microsoft.CodeAnalysis.Features" Version="4.4.0" />
26-
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="4.4.0" />
22+
<PackageReference Include="Microsoft.CodeAnalysis" Version="5.3.0" />
23+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="5.3.0" />
24+
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="5.3.0" />
25+
<PackageReference Include="Microsoft.CodeAnalysis.Features" Version="5.3.0" />
26+
<PackageReference Include="Microsoft.CodeAnalysis.Workspaces.MSBuild" Version="5.3.0" />
2727
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
2828
<PackageReference Include="System.CommandLine.Hosting" Version="0.4.0-alpha.22272.1" />
29-
<PackageReference Include="System.Configuration.ConfigurationManager" Version="7.0.0" />
29+
<PackageReference Include="System.Configuration.ConfigurationManager" Version="9.0.0" />
3030
</ItemGroup>
3131

3232
</Project>

ScipDotnet/ScipProjectIndexer.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ public ScipProjectIndexer(ILogger<ScipProjectIndexer> logger) =>
2020

2121
private void Restore(IndexCommandOptions options, FileInfo project)
2222
{
23-
var arguments = project.Extension.Equals(".sln") ? $"restore {project.FullName} /p:EnableWindowsTargeting=true" : "restore /p:EnableWindowsTargeting=true";
23+
var isSolution = project.Extension.Equals(".sln", StringComparison.OrdinalIgnoreCase)
24+
|| project.Extension.Equals(".slnx", StringComparison.OrdinalIgnoreCase);
25+
var arguments = isSolution ? $"restore {project.FullName} /p:EnableWindowsTargeting=true" : "restore /p:EnableWindowsTargeting=true";
2426
if (options.NugetConfigPath != null)
2527
{
2628
arguments += $" --configfile \"{options.NugetConfigPath.FullName}\"";
@@ -64,7 +66,9 @@ private void Restore(IndexCommandOptions options, FileInfo project)
6466
Restore(options, rootProject);
6567
}
6668

67-
var projects = (string.Equals(rootProject.Extension, ".csproj") || string.Equals(rootProject.Extension, ".vbproj")
69+
var isProjectFile = string.Equals(rootProject.Extension, ".csproj", StringComparison.OrdinalIgnoreCase)
70+
|| string.Equals(rootProject.Extension, ".vbproj", StringComparison.OrdinalIgnoreCase);
71+
var projects = (isProjectFile
6872
? new[]
6973
{
7074
await host.Services.GetRequiredService<MSBuildWorkspace>()
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFrameworks>net10.0;net9.0;net8.0</TargetFrameworks>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
</Project>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace SlnxTest;
2+
3+
public class Greeter
4+
{
5+
public string Greet(string name) => $"Hello, {name}!";
6+
}
7+
8+
public static class Program
9+
{
10+
public static void Main()
11+
{
12+
var greeter = new Greeter();
13+
Console.WriteLine(greeter.Greet("World"));
14+
}
15+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<Solution>
2+
<Project Path="Main/Main.csproj" />
3+
</Solution>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
namespace SlnxTest;
2+
// ^^^^^^^^ reference scip-dotnet nuget . . SlnxTest/
3+
4+
public class Greeter
5+
// ^^^^^^^ definition scip-dotnet nuget . . SlnxTest/Greeter#
6+
// documentation ```cs\nclass Greeter\n```
7+
{
8+
public string Greet(string name) => $"Hello, {name}!";
9+
// ^^^^^ definition scip-dotnet nuget . . SlnxTest/Greeter#Greet().
10+
// documentation ```cs\npublic string Greeter.Greet(string name)\n```
11+
// ^^^^ definition scip-dotnet nuget . . SlnxTest/Greeter#Greet().(name)
12+
// documentation ```cs\nstring name\n```
13+
// ^^^^ reference scip-dotnet nuget . . SlnxTest/Greeter#Greet().(name)
14+
}
15+
16+
public static class Program
17+
// ^^^^^^^ definition scip-dotnet nuget . . SlnxTest/Program#
18+
// documentation ```cs\nclass Program\n```
19+
{
20+
public static void Main()
21+
// ^^^^ definition scip-dotnet nuget . . SlnxTest/Program#Main().
22+
// documentation ```cs\npublic static void Program.Main()\n```
23+
{
24+
var greeter = new Greeter();
25+
// ^^^^^^^ definition local 0
26+
// documentation ```cs\nGreeter? greeter\n```
27+
// ^^^^^^^ reference scip-dotnet nuget . . SlnxTest/Greeter#
28+
Console.WriteLine(greeter.Greet("World"));
29+
// ^^^^^^^ reference scip-dotnet nuget System.Console 10.0.0.0 System/Console#
30+
// ^^^^^^^^^ reference scip-dotnet nuget System.Console 10.0.0.0 System/Console#WriteLine(+11).
31+
// ^^^^^^^ reference local 0
32+
// ^^^^^ reference scip-dotnet nuget . . SlnxTest/Greeter#Greet().
33+
}
34+
}

0 commit comments

Comments
 (0)