fix: support Visual Studio 2026 package loading

This commit is contained in:
2026-08-17 12:11:27 +08:00
parent d96c11ae3d
commit 97a2c61306
8 changed files with 61 additions and 26 deletions
@@ -7,11 +7,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Prophet.CoopWithDsh\SnapshotModels.cs" Link="SnapshotModels.cs" />
<Compile Include="..\Prophet.CoopWithDsh\SnapshotPublisher.cs" Link="SnapshotPublisher.cs" />
</ItemGroup>
</Project>
@@ -1,4 +1,5 @@
using System;
using System.IO;
using Xunit;
namespace Prophet.CoopWithDsh.Tests
@@ -19,5 +20,32 @@ namespace Prophet.CoopWithDsh.Tests
Assert.True(snapshot.Documents[0].Dirty);
Assert.Equal("2026-01-01T00:00:00.0000000Z", snapshot.UpdatedAt);
}
[Fact]
public void PublisherWritesProtocolV1WithoutExternalJsonRuntime()
{
var root = Path.Combine(Path.GetTempPath(), "coop-with-dsh-test-" + Guid.NewGuid().ToString("N"));
try
{
using (var publisher = new SnapshotPublisher(root))
{
var snapshot = SnapshotModel.Create(publisher.InstanceId, 42, null, new[] {
new OpenDocument { Path = "F:\\Project\\main.cs", Label = "main.cs", Dirty = true },
}, new DateTimeOffset(2026, 1, 1, 0, 0, 0, TimeSpan.Zero));
publisher.Publish(snapshot);
var json = File.ReadAllText(publisher.FilePath);
Assert.Contains("\"version\":1", json);
Assert.Contains("\"ide\":\"visualstudio\"", json);
Assert.Contains("\"documents\":[{", json);
Assert.Contains("\"dirty\":true", json);
Assert.DoesNotContain("\"workspace\"", json);
}
}
finally
{
if (Directory.Exists(root)) Directory.Delete(root, true);
}
}
}
}
@@ -8,6 +8,7 @@
<AssemblyName>Prophet.CoopWithDsh</AssemblyName>
<GenerateAssemblyInfo>true</GenerateAssemblyInfo>
<GeneratePkgDefFile>true</GeneratePkgDefFile>
<UseCodebase>true</UseCodebase>
<CreateVsixContainer>true</CreateVsixContainer>
<DeployExtension>false</DeployExtension>
<IncludeAssemblyInVSIXContainer>true</IncludeAssemblyInVSIXContainer>
@@ -17,7 +18,6 @@
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.SDK" Version="17.0.31902.203" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.VSSDK.BuildTools" Version="17.9.3168" PrivateAssets="all" GeneratePathProperty="true" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
<ItemGroup>
<None Include="source.extension.vsixmanifest" />
@@ -2,26 +2,28 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Newtonsoft.Json;
using System.Runtime.Serialization;
namespace Prophet.CoopWithDsh
{
[DataContract]
internal sealed class OpenDocument
{
[JsonProperty("path")] public string Path { get; set; } = string.Empty;
[JsonProperty("label")] public string Label { get; set; } = string.Empty;
[JsonProperty("dirty")] public bool Dirty { get; set; }
[DataMember(Name = "path", Order = 1)] public string Path { get; set; } = string.Empty;
[DataMember(Name = "label", Order = 2)] public string Label { get; set; } = string.Empty;
[DataMember(Name = "dirty", Order = 3)] public bool Dirty { get; set; }
}
[DataContract]
internal sealed class OpenFileSnapshot
{
[JsonProperty("version")] public int Version { get; set; } = 1;
[JsonProperty("instanceId")] public string InstanceId { get; set; } = string.Empty;
[JsonProperty("ide")] public string Ide { get; set; } = "visualstudio";
[JsonProperty("processId")] public int ProcessId { get; set; }
[JsonProperty("workspace", NullValueHandling = NullValueHandling.Ignore)] public string? Workspace { get; set; }
[JsonProperty("updatedAt")] public string UpdatedAt { get; set; } = string.Empty;
[JsonProperty("documents")] public IReadOnlyList<OpenDocument> Documents { get; set; } = Array.Empty<OpenDocument>();
[DataMember(Name = "version", Order = 1)] public int Version { get; set; } = 1;
[DataMember(Name = "instanceId", Order = 2)] public string InstanceId { get; set; } = string.Empty;
[DataMember(Name = "ide", Order = 3)] public string Ide { get; set; } = "visualstudio";
[DataMember(Name = "processId", Order = 4)] public int ProcessId { get; set; }
[DataMember(Name = "workspace", Order = 5, EmitDefaultValue = false)] public string? Workspace { get; set; }
[DataMember(Name = "updatedAt", Order = 6)] public string UpdatedAt { get; set; } = string.Empty;
[DataMember(Name = "documents", Order = 7)] public OpenDocument[] Documents { get; set; } = Array.Empty<OpenDocument>();
}
internal static class SnapshotModel
@@ -1,12 +1,13 @@
using System;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
using Newtonsoft.Json;
namespace Prophet.CoopWithDsh
{
internal sealed class SnapshotPublisher : IDisposable
{
private static readonly DataContractJsonSerializer Serializer = new DataContractJsonSerializer(typeof(OpenFileSnapshot));
private readonly object gate = new object();
internal string InstanceId { get; } = Guid.NewGuid().ToString("N");
internal string FilePath { get; }
@@ -26,7 +27,11 @@ namespace Prophet.CoopWithDsh
var directory = Path.GetDirectoryName(FilePath)!;
Directory.CreateDirectory(directory);
var temporary = FilePath + "." + Guid.NewGuid().ToString("N") + ".tmp";
File.WriteAllText(temporary, JsonConvert.SerializeObject(snapshot) + Environment.NewLine, new UTF8Encoding(false));
using (var stream = new MemoryStream())
{
Serializer.WriteObject(stream, snapshot);
File.WriteAllText(temporary, Encoding.UTF8.GetString(stream.ToArray()) + Environment.NewLine, new UTF8Encoding(false));
}
try
{
if (File.Exists(FilePath)) File.Replace(temporary, FilePath, null);
@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Id="Prophet.CoopWithDsh.1e02d547-499b-41ed-a25e-b356a9687e31" Version="0.1.0" Language="en-US" Publisher="Prophet" />
<Identity Id="Prophet.CoopWithDsh.1e02d547-499b-41ed-a25e-b356a9687e31" Version="0.1.1" Language="en-US" Publisher="Prophet" />
<DisplayName>DSH Open File Bridge</DisplayName>
<Description xml:space="preserve">Publishes Visual Studio open file metadata for DeepSeek Harness @ references.</Description>
<License>LICENSE.txt</License>
</Metadata>
<Installation>
<InstallationTarget Id="Microsoft.VisualStudio.Community" Version="[17.0,18.0)"><ProductArchitecture>amd64</ProductArchitecture></InstallationTarget>
<InstallationTarget Id="Microsoft.VisualStudio.Pro" Version="[17.0,18.0)"><ProductArchitecture>amd64</ProductArchitecture></InstallationTarget>
<InstallationTarget Id="Microsoft.VisualStudio.Enterprise" Version="[17.0,18.0)"><ProductArchitecture>amd64</ProductArchitecture></InstallationTarget>
<InstallationTarget Id="Microsoft.VisualStudio.Community" Version="[17.0,19.0)"><ProductArchitecture>amd64</ProductArchitecture></InstallationTarget>
<InstallationTarget Id="Microsoft.VisualStudio.Pro" Version="[17.0,19.0)"><ProductArchitecture>amd64</ProductArchitecture></InstallationTarget>
<InstallationTarget Id="Microsoft.VisualStudio.Enterprise" Version="[17.0,19.0)"><ProductArchitecture>amd64</ProductArchitecture></InstallationTarget>
</Installation>
<Dependencies />
<Prerequisites>
<Prerequisite Id="Microsoft.VisualStudio.Component.CoreEditor" Version="[17.0,18.0)" DisplayName="Visual Studio core editor" />
<Prerequisite Id="Microsoft.VisualStudio.Component.CoreEditor" Version="[17.0,19.0)" DisplayName="Visual Studio core editor" />
</Prerequisites>
<Assets>
<Asset Type="Microsoft.VisualStudio.VsPackage" d:Source="Project" d:ProjectName="Prophet.CoopWithDsh" Path="|Prophet.CoopWithDsh;PkgdefProjectOutputGroup|" />
+3 -3
View File
@@ -1,12 +1,12 @@
# DSH Open File Bridge for Visual Studio 2022
# DSH Open File Bridge for Visual Studio
This VSIX is a background-loaded `AsyncPackage`. Each Visual Studio process enumerates its own `IVsRunningDocumentTable`, listens for RDT changes, and publishes one protocol-v1 snapshot. This avoids the ambiguity and integrity-level failures of attaching externally through EnvDTE/ROT.
This VSIX supports Visual Studio 2022 (17.x) and Visual Studio 2026 (18.x). It is a background-loaded `AsyncPackage`. Each Visual Studio process enumerates its own `IVsRunningDocumentTable`, listens for RDT changes, and publishes one protocol-v1 snapshot. This avoids the ambiguity and integrity-level failures of attaching externally through EnvDTE/ROT.
Only absolute, existing local files are published. Dirty state is read through `IVsPersistDocData`; source buffers are never copied.
## Build and test
Use a Visual Studio Developer PowerShell or a machine with the VS 2022 extension workload:
Use a Visual Studio Developer PowerShell or a machine with the Visual Studio extension workload:
```powershell
dotnet restore CoopWithDsh.sln