| 1 | #nullable enable |
| 2 | |
| 3 | namespace CascadeIDE.Services.Intercom; |
| 4 | |
| 5 | /// <summary>Find по коду в активной ветке (ADR 0137).</summary> |
| 6 | internal static class IntercomCorrespondenceOperations |
| 7 | { |
| 8 | internal sealed record FindError(string Kind, string Message); |
| 9 | |
| 10 | internal sealed record FindExecutionResult( |
| 11 | IReadOnlyList<IntercomMessageCodeCorrespondenceProjector.MatchHit>? Hits, |
| 12 | IReadOnlyList<int>? Ordinals, |
| 13 | int? SelectedOrdinal, |
| 14 | int BranchMessageCount, |
| 15 | FindError? Error) |
| 16 | { |
| 17 | public static FindExecutionResult Fail(string kind, string message) => |
| 18 | new(null, null, null, 0, new FindError(kind, message)); |
| 19 | } |
| 20 | |
| 21 | internal static FindExecutionResult ExecuteFind( |
| 22 | IntercomCodeRefQuery query, |
| 23 | string? workspace, |
| 24 | bool isOverviewMode, |
| 25 | int laneMessageCount, |
| 26 | IReadOnlyList<IntercomMessageCodeCorrespondenceProjector.LaneMessage> lane, |
| 27 | IReadOnlyList<IntercomMessageRangeRelatedProjector.ExplicitRelate> explicitForThread, |
| 28 | Func<int, int, string> selectOrdinalRange) |
| 29 | { |
| 30 | if (isOverviewMode) |
| 31 | { |
| 32 | return FindExecutionResult.Fail( |
| 33 | "overview_mode", |
| 34 | "Открой тему (detail): /intercom topic open или клик по карточке."); |
| 35 | } |
| 36 | |
| 37 | if (laneMessageCount == 0) |
| 38 | return FindExecutionResult.Fail("empty_lane", "В активной ветке нет сообщений."); |
| 39 | |
| 40 | var entries = IntercomMessageCodeCorrespondenceProjector.BuildCombined(lane, explicitForThread); |
| 41 | var hits = IntercomMessageCodeCorrespondenceProjector.Find(entries, query, workspace); |
| 42 | if (hits.Count == 0) |
| 43 | { |
| 44 | return FindExecutionResult.Fail( |
| 45 | "no_hits", |
| 46 | "Нет сообщений в ветке, связанных с этим фрагментом кода."); |
| 47 | } |
| 48 | |
| 49 | var ordinals = hits.Select(h => h.Ordinal).OrderBy(o => o).ToList(); |
| 50 | int? selected = null; |
| 51 | if (string.Equals(selectOrdinalRange(ordinals[0], ordinals[^1]), "OK", StringComparison.Ordinal)) |
| 52 | selected = ordinals[^1]; |
| 53 | |
| 54 | return new FindExecutionResult(hits, ordinals, selected, laneMessageCount, null); |
| 55 | } |
| 56 | |
| 57 | internal static string FormatFindResult(FindExecutionResult result) |
| 58 | { |
| 59 | if (result.Error is { } err) |
| 60 | return err.Message; |
| 61 | |
| 62 | var ordinals = result.Ordinals!; |
| 63 | var label = ordinals.Count == 1 |
| 64 | ? $"#{ordinals[0]}" |
| 65 | : $"#{ordinals[0]}–#{ordinals[^1]}"; |
| 66 | return $"Связанные сообщения: {label} ({result.Hits!.Count}). Активно #{ordinals[^1]}."; |
| 67 | } |
| 68 | } |
| 69 | |