Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions .config/dotnet/common.props
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,16 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="5.6.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="5.6.0" PrivateAssets="all" />
<PackageReference
Include="Microsoft.CodeAnalysis.CSharp"
Version="5.6.0"
PrivateAssets="all"
/>
<PackageReference
Include="Microsoft.CodeAnalysis.Analyzers"
Version="5.6.0"
PrivateAssets="all"
/>
<PackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="5.6.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
Expand Down
19 changes: 9 additions & 10 deletions src/editors/rider/src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,39 @@
<extensions defaultExtensionNs="com.intellij">
<!-- LSP server registration — extension point lives in the
com.intellij.modules.lsp module. -->
<platform.lsp.serverSupportProvider
implementation="com.forgelsp.rider.lsp.ForgeLspServerSupportProvider"/>
<platform.lsp.serverSupportProvider implementation="com.forgelsp.rider.lsp.ForgeLspServerSupportProvider" />

<!-- Solution Explorer tool window. Anchored to the left so it
sits next to Rider's built-in Solution Explorer. -->
<toolWindow
id="Forge Solution"
anchor="left"
icon="/icons/forge.svg"
factoryClass="com.forgelsp.rider.toolwindow.ForgeSolutionToolWindowFactory"/>
factoryClass="com.forgelsp.rider.toolwindow.ForgeSolutionToolWindowFactory"
/>

<!-- NuGet Package Browser tool window. Anchored to the bottom
so it doesn't fight with the solution tree. -->
<toolWindow
id="Forge NuGet"
anchor="bottom"
icon="/icons/forge.svg"
factoryClass="com.forgelsp.rider.toolwindow.nuget.ForgeNuGetToolWindowFactory"/>
factoryClass="com.forgelsp.rider.toolwindow.nuget.ForgeNuGetToolWindowFactory"
/>

<!-- Notification group used by every Forge action. Must be
registered so user toasts for install/uninstall/restore
actually surface. -->
<notificationGroup
id="Forge"
displayType="BALLOON"/>
<notificationGroup id="Forge" displayType="BALLOON" />

<!-- Project-level settings UI at Settings → Tools → Forge. -->
<projectConfigurable
parentId="tools"
id="com.forgelsp.rider.settings"
displayName="Forge"
instance="com.forgelsp.rider.settings.ForgeSettingsConfigurable"/>
instance="com.forgelsp.rider.settings.ForgeSettingsConfigurable"
/>

<projectService
serviceImplementation="com.forgelsp.rider.settings.ForgeSettings"/>
<projectService serviceImplementation="com.forgelsp.rider.settings.ForgeSettings" />
</extensions>
</idea-plugin>
3 changes: 1 addition & 2 deletions src/sharplsp/tests/fixtures/NuGetTest/NuGetTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,5 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />

</ItemGroup>
</ItemGroup>
</Project>
63 changes: 40 additions & 23 deletions src/sharplsp/tests/fixtures/ProfileTarget/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
using System.Text.Json;

using var cts = new CancellationTokenSource();
Console.CancelKeyPress += (_, e) => { e.Cancel = true; cts.Cancel(); };
Console.CancelKeyPress += (_, e) =>
{
e.Cancel = true;
cts.Cancel();
};

// Self-terminate when orphaned: if the parent (the test host) dies abnormally —
// e.g. nextest SIGKILLs a timed-out test — Rust-side `Drop` cleanup never runs
Expand All @@ -27,7 +31,10 @@
Task.Run(() => StringBuilderAllocation(cts.Token), cts.Token),
};

try { await Task.WhenAll(tasks).ConfigureAwait(false); }
try
{
await Task.WhenAll(tasks).ConfigureAwait(false);
}
catch (OperationCanceledException) { }

// Cancel `cts` as soon as this process is reparented away from its original
Expand Down Expand Up @@ -102,7 +109,9 @@ static void StartWindowsParentDeathWatchdog(CancellationTokenSource cts)
{
// Wait failure means we can no longer observe the ancestor;
// treat it as death so we never linger as an unwatched orphan.
WatchdogLog($"wait on ancestor {watched.Id} failed ({ex.GetType().Name}) -> cancel");
WatchdogLog(
$"wait on ancestor {watched.Id} failed ({ex.GetType().Name}) -> cancel"
);
}

cts.Cancel();
Expand Down Expand Up @@ -220,19 +229,22 @@ static void LockContention(CancellationToken ct)
var queue = new Queue<int>();
var locker = new object();
const int maxQueueDepth = 256;
var producer = Task.Run(() =>
{
var i = 0;
while (!ct.IsCancellationRequested)
var producer = Task.Run(
() =>
{
lock (locker)
var i = 0;
while (!ct.IsCancellationRequested)
{
if (queue.Count < maxQueueDepth)
queue.Enqueue(i++);
lock (locker)
{
if (queue.Count < maxQueueDepth)
queue.Enqueue(i++);
}
Thread.SpinWait(128);
}
Thread.SpinWait(128);
}
}, ct);
},
ct
);

while (!ct.IsCancellationRequested)
{
Expand Down Expand Up @@ -261,7 +273,8 @@ static void DeepCallStack(CancellationToken ct)
static int SumCharValues(string text)
{
var sum = 0;
foreach (var c in text) sum += c;
foreach (var c in text)
sum += c;
return sum;
}

Expand All @@ -272,9 +285,7 @@ static void StringBuilderAllocation(CancellationToken ct)
while (!ct.IsCancellationRequested)
{
iteration++;
_ = iteration % 2 == 0
? BuildWithStringBuilder(64)
: BuildWithConcatenation(64);
_ = iteration % 2 == 0 ? BuildWithStringBuilder(64) : BuildWithConcatenation(64);
}
}

Expand All @@ -300,7 +311,8 @@ static string BuildLargeJsonPayload(int entries)
sb.Append('{');
for (var i = 0; i < entries; i++)
{
if (i > 0) sb.Append(',');
if (i > 0)
sb.Append(',');
sb.Append(System.FormattableString.Invariant($"\"key{i}\":\"value{i}\""));
}
sb.Append('}');
Expand All @@ -318,14 +330,16 @@ internal static class NativeMethods
// (SYSLIB1062), not worth enabling for one getppid (suppressed in the csproj).
[System.Runtime.InteropServices.DllImport("libc")]
[System.Runtime.InteropServices.DefaultDllImportSearchPaths(
System.Runtime.InteropServices.DllImportSearchPath.System32)]
System.Runtime.InteropServices.DllImportSearchPath.System32
)]
internal static extern int getppid();

// Windows has no getppid(2); the parent PID lives in
// PROCESS_BASIC_INFORMATION.InheritedFromUniqueProcessId, reachable only
// via NtQueryInformationProcess (info class 0 = ProcessBasicInformation).
[System.Runtime.InteropServices.StructLayout(
System.Runtime.InteropServices.LayoutKind.Sequential)]
System.Runtime.InteropServices.LayoutKind.Sequential
)]
private struct ProcessBasicInformation
{
public IntPtr ExitStatus;
Expand All @@ -338,13 +352,15 @@ private struct ProcessBasicInformation

[System.Runtime.InteropServices.DllImport("ntdll.dll")]
[System.Runtime.InteropServices.DefaultDllImportSearchPaths(
System.Runtime.InteropServices.DllImportSearchPath.System32)]
System.Runtime.InteropServices.DllImportSearchPath.System32
)]
private static extern int NtQueryInformationProcess(
IntPtr processHandle,
int processInformationClass,
ref ProcessBasicInformation processInformation,
int processInformationLength,
out int returnLength);
out int returnLength
);

/// <summary>Creator (parent) PID of the process behind <paramref name="processHandle"/>
/// on Windows, or -1 when it cannot be determined.</summary>
Expand All @@ -356,7 +372,8 @@ internal static int GetParentPid(IntPtr processHandle)
0,
ref info,
System.Runtime.InteropServices.Marshal.SizeOf<ProcessBasicInformation>(),
out _);
out _
);
return status == 0 ? unchecked((int)info.InheritedFromUniqueProcessId.ToInt64()) : -1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,37 @@ public async Task ResolveCompletion_after_completion_returns_resolve_result()
Assert.Empty(guarded.AdditionalEdits);
}

[Fact]
public async Task ResolveCompletion_with_empty_span_skips_primary_edit_in_additional_edits()
{
// GitHub double-insertion bug: triggering completion immediately after a dot
// produces a length-0 completion span. ResolveCompletion must use IntersectsWith
// to correctly skip the primary edit so it doesn't get double-applied.
using var manager = await OpenAsync();

// Add a dot to trigger completion on an empty span.
var newSource = Source.Replace("Total = result;", "Total = result.;");
await manager.UpdateDocumentTextAsync(_sourcePath, newSource);

// Position: line 19, char 23 (immediately after `result.`)
var completions = await manager.GetCompletionsAsync(_sourcePath, 19, 23);
var items = Unwrap(completions);

// Find a valid completion like "ToString"
var toString = items.Find(item => item.Label == "ToString");
Assert.NotNull(toString);

var resolved = await manager.ResolveCompletionAsync(
toString!.Index,
CancellationToken.None
);
Assert.NotNull(resolved);

// The primary edit (inserting "ToString") MUST be skipped. AdditionalEdits
// should be empty (or at least not contain "ToString" at the cursor).
Assert.DoesNotContain(resolved.AdditionalEdits, edit => edit.NewText == "ToString");
}

[Fact]
public async Task CodeLenses_report_reference_counts_for_members()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,11 @@ public async Task FileBasedApp_closure_file_count_is_bounded()
}
var app = Write("Many.cs", "#:include many/*.cs\nConsole.WriteLine(1);\n");

var closure = await DocumentClosure.ExpandFileBasedAsync(app, CancellationToken.None);
var closure = await DocumentClosure.ExpandFileBasedAsync(
app,
rootText: null,
CancellationToken.None
);

Assert.Equal(64, closure.Files.Count);
Assert.Contains(
Expand All @@ -415,7 +419,11 @@ public async Task FileBasedApp_include_depth_is_bounded()
}
var app = Write("Chain.cs", "#:include chain0.cs\nConsole.WriteLine(1);\n");

var closure = await DocumentClosure.ExpandFileBasedAsync(app, CancellationToken.None);
var closure = await DocumentClosure.ExpandFileBasedAsync(
app,
rootText: null,
CancellationToken.None
);

Assert.Contains(
closure.Issues,
Expand Down
Loading
Loading