Skip to content
Merged
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
19 changes: 19 additions & 0 deletions changelog.d/unreleased/3658.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
category: fixed
issues:
- 3658
affected:
- src/CodeIndex/Cli/SuggestionStore.cs
- src/CodeIndex/Lsp/LspServer.cs
- src/CodeIndex/Mcp/McpToolHandlers.cs
- tests/CodeIndex.Tests/McpServerTests.cs
- tests/CodeIndex.Tests/SuggestionStoreTests.cs
---

## English

- Route caller cancellation through MCP status update checks, add a cancellable suggestion submission overload, and document compatibility no-token LSP/suggestion wrappers.

## 日本語

- MCP status update check に caller cancellation を渡し、キャンセル可能な suggestion submission overload を追加し、LSP / suggestion の no-token 互換 wrapper を明記しました。
18 changes: 18 additions & 0 deletions changelog.d/unreleased/3705.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
category: fixed
issues:
- 3705
affected:
- src/CodeIndex/Cli/IndexCommandRunner.FullScan.cs
- src/CodeIndex/Cli/IndexCommandRunner.UpdateTargets.cs
- src/CodeIndex/Mcp/HttpMcpTransport.cs
- tests/CodeIndex.Tests/IndexCommandRunnerHeartbeatTests.cs
---

## English

- Route JSON index heartbeat tasks and HTTP MCP request-handler tasks through observed background work while keeping expected cancellation quiet and preserving handler-slot cleanup during shutdown.

## 日本語

- JSON index heartbeat タスクと HTTP MCP request-handler タスクを監視対象の background work に通し、期待されるキャンセルでは診断を出さず、shutdown 中も handler slot cleanup を保つようにしました。
16 changes: 16 additions & 0 deletions changelog.d/unreleased/3743.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
category: fixed
issues:
- 3743
affected:
- src/CodeIndex/Lsp/LspServer.cs
- tests/CodeIndex.Tests/LspServerTests.cs
---

## English

- Avoid repeated full JSON serialization passes while trimming oversized LSP document-symbol responses.

## 日本語

- サイズ超過した LSP document-symbol response を trim する際、root 全体の JSON serialization を繰り返さないようにしました。
16 changes: 16 additions & 0 deletions changelog.d/unreleased/3758.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
category: fixed
issues:
- 3758
affected:
- src/CodeIndex/Lsp/LspServer.cs
- tests/CodeIndex.Tests/LspServerTests.cs
---

## English

- Cap LSP document-symbol materialization before sorting and emit diagnostics when candidate materialization is truncated.

## 日本語

- LSP document-symbol の候補 materialization を sort 前に制限し、候補が truncation された場合は diagnostics を出すようにしました。
16 changes: 16 additions & 0 deletions changelog.d/unreleased/3760.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
category: fixed
issues:
- 3760
affected:
- src/CodeIndex/Diagnostics/BackgroundTaskObserver.cs
- tests/CodeIndex.Tests/BackgroundTaskObserverTests.cs
---

## English

- Honor shutdown cancellation when scheduling observed background work.

## 日本語

- 監視対象のバックグラウンド処理をスケジュールする際に shutdown cancellation を反映するようにしました。
33 changes: 6 additions & 27 deletions src/CodeIndex/Cli/IndexCommandRunner.FullScan.cs
Original file line number Diff line number Diff line change
Expand Up @@ -672,33 +672,12 @@ private static (CancellationTokenSource Cts, Task Task)? StartFullScanJsonPhaseH
string phase,
Func<string?>? detailProvider = null)
{
if (!options.Json || options.Quiet)
return null;

var cts = new CancellationTokenSource();
var token = cts.Token;
var task = Task.Run(async () =>
{
while (!token.IsCancellationRequested)
{
try
{
await Task.Delay(TimeSpan.FromSeconds(5), token).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
break;
}

if (token.IsCancellationRequested)
break;

var detail = detailProvider?.Invoke();
var suffix = string.IsNullOrWhiteSpace(detail) ? string.Empty : $": {detail}";
ConsoleUi.TryWriteErrorLine($"cdidx: still {phase}{suffix}...");
}
}, token);
return (cts, task);
return StartObservedJsonPhaseHeartbeat(
options.Json && !options.Quiet,
"cdidx-index",
phase,
ConsoleUi.TryWriteErrorLine,
detailProvider);
}

private static void StopFullScanJsonPhaseHeartbeat((CancellationTokenSource Cts, Task Task)? heartbeat)
Expand Down
72 changes: 52 additions & 20 deletions src/CodeIndex/Cli/IndexCommandRunner.UpdateTargets.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Text.Json;
using CodeIndex.Diagnostics;

namespace CodeIndex.Cli;

Expand Down Expand Up @@ -122,33 +123,64 @@ private static (CancellationTokenSource Cts, Task Task)? StartIndexJsonPhaseHear
string phase,
Func<string?>? detailProvider = null)
{
if (!options.Json || options.Quiet)
return StartObservedJsonPhaseHeartbeat(
options.Json && !options.Quiet,
"cdidx-index",
phase,
ConsoleUi.TryWriteErrorLine,
detailProvider);
}

internal static (CancellationTokenSource Cts, Task Task)? StartObservedJsonPhaseHeartbeat(
bool enabled,
string component,
string phase,
Action<string> messageWriter,
Func<string?>? detailProvider = null,
TimeSpan? interval = null,
Action<string>? warningWriter = null)
{
if (!enabled)
return null;

ArgumentNullException.ThrowIfNull(messageWriter);

var cts = new CancellationTokenSource();
var token = cts.Token;
var task = Task.Run(async () =>
var heartbeatInterval = interval ?? TimeSpan.FromSeconds(5);
var task = BackgroundTaskObserver.Run(
token => RunObservedJsonPhaseHeartbeatLoop(phase, messageWriter, detailProvider, heartbeatInterval, token),
component,
$"{phase} heartbeat",
cts.Token,
warningWriter);
return (cts, task);
}

private static async Task RunObservedJsonPhaseHeartbeatLoop(
string phase,
Action<string> messageWriter,
Func<string?>? detailProvider,
TimeSpan interval,
CancellationToken token)
{
while (!token.IsCancellationRequested)
{
while (!token.IsCancellationRequested)
try
{
try
{
await Task.Delay(TimeSpan.FromSeconds(5), token).ConfigureAwait(false);
}
catch (OperationCanceledException)
{
break;
}
await Task.Delay(interval, token).ConfigureAwait(false);
}
catch (OperationCanceledException) when (token.IsCancellationRequested)
{
break;
}

if (token.IsCancellationRequested)
break;
if (token.IsCancellationRequested)
break;

var detail = detailProvider?.Invoke();
var suffix = string.IsNullOrWhiteSpace(detail) ? string.Empty : $": {detail}";
CommandErrorWriter.WriteStderr($"cdidx: still {phase}{suffix}...");
}
}, token);
return (cts, task);
var detail = detailProvider?.Invoke();
var suffix = string.IsNullOrWhiteSpace(detail) ? string.Empty : $": {detail}";
messageWriter($"cdidx: still {phase}{suffix}...");
}
}

private static void StopIndexJsonPhaseHeartbeat((CancellationTokenSource Cts, Task Task)? heartbeat)
Expand Down
13 changes: 12 additions & 1 deletion src/CodeIndex/Cli/SuggestionStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,14 @@ public record SubmitAttemptResult(string? IssueUrl, string? Error, DateTime? Nex
/// Add a suggestion under the file lock, then attempt GitHub submission outside the lock.
/// The store reserves the attempt before releasing the lock so concurrent callers do not
/// also submit the same unsubmitted duplicate while the remote API is slow.
/// This compatibility wrapper has no caller cancellation; use the overload that accepts
/// <see cref="CancellationToken"/> when cancellation should be observed.
/// 提案をファイルロック内で追加し、その後 GitHub 送信はロック外で試行する。
/// remote API が遅い間に並行呼び出しが同じ未送信重複を送信しないよう、
/// ロック解放前に送信試行を予約する。
/// この互換 wrapper は caller cancellation を持たない。キャンセルを監視する場合は
/// <see cref="TryAddAndSubmit(SuggestionRecord, Func{SuggestionRecord, SubmitAttemptResult}?, CancellationToken)"/>
/// を使う。
/// </summary>
/// <param name="record">The suggestion to add / 追加する提案</param>
/// <param name="submitToGitHub">
Expand All @@ -218,13 +223,19 @@ public record SubmitAttemptResult(string? IssueUrl, string? Error, DateTime? Nex
/// 未送信の重複)にのみロック外で呼ばれる。成功時は Issue URL を返す。
/// </param>
public AddAndSubmitResult TryAddAndSubmit(SuggestionRecord record, Func<SuggestionRecord, SubmitAttemptResult>? submitToGitHub)
=> TryAddAndSubmit(record, submitToGitHub, CancellationToken.None);

public AddAndSubmitResult TryAddAndSubmit(
SuggestionRecord record,
Func<SuggestionRecord, SubmitAttemptResult>? submitToGitHub,
CancellationToken cancellationToken)
{
return TryAddAndSubmitAsync(
record,
submitToGitHub == null
? null
: (r, _) => Task.FromResult(submitToGitHub(r)),
CancellationToken.None).GetAwaiter().GetResult();
cancellationToken).GetAwaiter().GetResult();
}

public async Task<AddAndSubmitResult> TryAddAndSubmitAsync(
Expand Down
4 changes: 2 additions & 2 deletions src/CodeIndex/Diagnostics/BackgroundTaskObserver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal static Task Run(
{
ArgumentNullException.ThrowIfNull(action);

return Observe(Task.Run(action, CancellationToken.None), component, operation, warningWriter);
return Run(_ => action(), component, operation, CancellationToken.None, warningWriter);
}

internal static Task Run(
Expand All @@ -27,7 +27,7 @@ internal static Task Run(
{
ArgumentNullException.ThrowIfNull(action);

return Observe(Task.Run(() => action(cancellationToken), CancellationToken.None), component, operation, warningWriter);
return Observe(Task.Run(() => action(cancellationToken), cancellationToken), component, operation, warningWriter);
}

internal static Task Observe(
Expand Down
Loading
Loading