Forge
csharp4405de34
1#nullable enable
2using System.Text.Json;
3
4using CascadeIDE.Models.Editor;
5
6namespace CascadeIDE.Services;
7
8/// <summary>
9/// Сборка JSON-args для параметрики с двумя int-слотами (диапазон строк) по <c>command_id</c> из каталога.
10/// Каталог не содержит <c>binder</c> (ADR 0109): соответствие «команда → форма args» живёт в коде в этом типе.
11/// </summary>
12internal static class ParametricLineRangeArgsBuilder
13{
14 public static bool TryBuild(
15 ParametricIntentMelody.ParsedLineRange parsed,
16 string? currentFilePath,
17 string? editorText,
18 out string commandId,
19 out string argsJson,
20 out string error)
21 {
22 commandId = "";
23 argsJson = "";
24 error = "";
25
26 if (!EditorDocumentPath.TryCreate(currentFilePath, out var documentPath, out error))
27 return false;
28
29 var text = editorText ?? "";
30 var lines = text.Split('\n');
31 var lineCount = lines.Length;
32
33 var startLine = parsed.Lines.Start.Value;
34 var endLine = parsed.Lines.End.Value;
35
36 if (endLine > lineCount)
37 {
38 error = $"Диапазон выходит за пределы файла: всего строк {lineCount}.";
39 return false;
40 }
41
42 if (!IntentMelodyCatalog.TryGetRoot(parsed.Alias, out var entry) || entry.Shape != IntentMelodyShape.Parametric)
43 {
44 error = $"Alias '{parsed.Alias}' отсутствует в каталоге melody или не параметрический.";
45 return false;
46 }
47
48 var catalogCmd = entry.CommandId.Trim();
49 if (string.IsNullOrWhiteSpace(catalogCmd))
50 {
51 error = $"У корня '{parsed.Alias}' пустой command_id в каталоге.";
52 return false;
53 }
54
55 var ts = entry.TailSignature ?? "";
56 if (IntentMelodyTailSemantics.HasUrlSlot(ts)
57 || IntentMelodyTailSemantics.CountDelimitedNumericSlots(ts) != 2)
58 {
59 error = $"Форма tail_signature для '{parsed.Alias}' ожидает ровно два числовых слота (напр. <start:ln>:<end:ln> или устар. <start:int>:<end:int>) для диапазона строк.";
60 return false;
61 }
62
63 switch (catalogCmd)
64 {
65 case IdeCommands.Select:
66 if (!ColumnNumber.TryCreate(ColumnNumber.MinimumOneBasedInclusive, out var startCol)
67 || !ColumnNumber.TryCreate(lines[endLine - 1].Length + 1, out var endCol))
68 {
69 error = "Не удалось вычислить границы колонок для выделения.";
70 return false;
71 }
72
73 commandId = catalogCmd;
74 argsJson = JsonSerializer.Serialize(new
75 {
76 file_path = documentPath.Value,
77 start_line = startLine,
78 start_column = startCol.Value,
79 end_line = endLine,
80 end_column = endCol.Value,
81 });
82 return true;
83
84 case IdeCommands.ApplyEdit:
85 var lastLine = endLine == lineCount;
86 if (!ColumnNumber.TryCreate(ColumnNumber.MinimumOneBasedInclusive, out var applyStartCol))
87 {
88 error = "Не удалось вычислить начальную колонку для правки.";
89 return false;
90 }
91
92 int endLineWire = lastLine ? endLine : endLine + 1;
93 int endColWire = lastLine ? lines[endLine - 1].Length + 1 : ColumnNumber.MinimumOneBasedInclusive;
94 if (!ColumnNumber.TryCreate(endColWire, out var applyEndCol))
95 {
96 error = "Не удалось вычислить конечную колонку для правки.";
97 return false;
98 }
99
100 commandId = catalogCmd;
101 argsJson = JsonSerializer.Serialize(new
102 {
103 file_path = documentPath.Value,
104 start_line = startLine,
105 start_column = applyStartCol.Value,
106 end_line = endLineWire,
107 end_column = applyEndCol.Value,
108 new_text = "",
109 });
110 return true;
111
112 default:
113 error = $"Параметрический диапазон строк для '{parsed.Alias}' (command_id '{catalogCmd}') ещё не подключён к сборке args.";
114 return false;
115 }
116 }
117}
118
View only · write via MCP/CIDE