pando-ai 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,11 @@
4
4
  <TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks>
5
5
  <Nullable>enable</Nullable>
6
6
  <LangVersion>latest</LangVersion>
7
+ <EnableDefaultCompileItems>false</EnableDefaultCompileItems>
7
8
  </PropertyGroup>
9
+ <ItemGroup>
10
+ <Compile Include="src/**/*.cs" />
11
+ </ItemGroup>
8
12
  <ItemGroup>
9
13
  <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.10.0" />
10
14
  <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.10.0" />
@@ -29,6 +29,12 @@ namespace Pando.CSharpIndexer
29
29
  [JsonPropertyName("qualifiedName")] public string? QualifiedName { get; set; }
30
30
  [JsonPropertyName("scopeChain")] public List<string>? ScopeChain { get; set; }
31
31
  [JsonPropertyName("symbolKey")] public string? SymbolKey { get; set; }
32
+ [JsonPropertyName("parentIndex")] public int? ParentIndex { get; set; }
33
+ [JsonPropertyName("role")] public string? Role { get; set; }
34
+ [JsonPropertyName("indexInParent")] public int? IndexInParent { get; set; }
35
+ [JsonPropertyName("depth")] public int? Depth { get; set; }
36
+ [JsonPropertyName("bodyStart")] public int? BodyStart { get; set; }
37
+ [JsonPropertyName("bodyEnd")] public int? BodyEnd { get; set; }
32
38
  [JsonPropertyName("metadata")] public Dictionary<string, object?>? Metadata { get; set; }
33
39
  }
34
40
 
@@ -156,7 +162,8 @@ namespace Pando.CSharpIndexer
156
162
  return resolved.Error == null ? 0 : 2;
157
163
  }
158
164
 
159
- var result = BuildParseResult(root, content);
165
+ var emitMode = parsed.TryGetValue("emitMode", out var mode) && !string.IsNullOrWhiteSpace(mode) ? mode! : "light";
166
+ var result = BuildParseResult(root, content, emitMode);
160
167
  WriteOutput(parsed, result);
161
168
  return 0;
162
169
  }
@@ -218,7 +225,8 @@ namespace Pando.CSharpIndexer
218
225
  }
219
226
  try
220
227
  {
221
- var result = ParseFile(file!);
228
+ var emitMode = GetString(item, "emitMode") ?? "light";
229
+ var result = ParseFile(file!, emitMode);
222
230
  results.Add(new { file, ok = true, result });
223
231
  }
224
232
  catch (Exception ex)
@@ -237,7 +245,8 @@ namespace Pando.CSharpIndexer
237
245
  }
238
246
  try
239
247
  {
240
- var result = ParseFile(singleFile!);
248
+ var emitMode = GetString(root, "emitMode") ?? "light";
249
+ var result = ParseFile(singleFile!, emitMode);
241
250
  WriteServerResponse(new { id, ok = true, result }, options);
242
251
  }
243
252
  catch (Exception ex)
@@ -347,12 +356,12 @@ namespace Pando.CSharpIndexer
347
356
  return null;
348
357
  }
349
358
 
350
- private static CliParseResult ParseFile(string file)
359
+ private static CliParseResult ParseFile(string file, string emitMode)
351
360
  {
352
361
  var content = File.ReadAllText(file);
353
362
  var tree = CSharpSyntaxTree.ParseText(content, path: file);
354
363
  var root = tree.GetCompilationUnitRoot();
355
- return BuildParseResult(root, content);
364
+ return BuildParseResult(root, content, emitMode);
356
365
  }
357
366
 
358
367
  private static CliResolvedSpan ResolveFile(string file, string nodePath)
@@ -414,7 +423,7 @@ namespace Pando.CSharpIndexer
414
423
  }
415
424
  }
416
425
 
417
- private static CliParseResult BuildParseResult(CompilationUnitSyntax root, string content)
426
+ private static CliParseResult BuildParseResult(CompilationUnitSyntax root, string content, string emitMode)
418
427
  {
419
428
  var result = new CliParseResult();
420
429
  var semanticModel = TryCreateSemanticModel(root);
@@ -529,12 +538,128 @@ namespace Pando.CSharpIndexer
529
538
  }
530
539
  }
531
540
 
541
+ if (IsFullEmitMode(emitMode))
542
+ {
543
+ AddFullSyntaxNodes(result, root);
544
+ AddCommentTriviaNodes(result, root);
545
+ }
546
+
532
547
  EnrichNodeHierarchy(result.Nodes, root);
533
548
 
534
549
  result.ModulePublic = publicNames.ToList();
535
550
  return result;
536
551
  }
537
552
 
553
+ private static bool IsFullEmitMode(string? emitMode)
554
+ {
555
+ return string.Equals(emitMode, "full", StringComparison.OrdinalIgnoreCase);
556
+ }
557
+
558
+ private static void AddFullSyntaxNodes(CliParseResult result, CompilationUnitSyntax root)
559
+ {
560
+ var emittedKeys = result.Nodes
561
+ .Select(n => NodeKey(n.Start, n.End, n.RawKind ?? n.Kind))
562
+ .ToHashSet(StringComparer.Ordinal);
563
+
564
+ foreach (var node in EnumerateSyntaxNodesIncludingRoot(root))
565
+ {
566
+ var rawKind = node.Kind().ToString();
567
+ var start = NodeStart(node);
568
+ var end = NodeEnd(node);
569
+ var key = NodeKey(start, end, rawKind);
570
+ if (emittedKeys.Contains(key)) { continue; }
571
+ result.Nodes.Add(new CliNode
572
+ {
573
+ Name = null,
574
+ Kind = rawKind,
575
+ RawKind = rawKind,
576
+ RawKindSource = "roslyn",
577
+ SemanticKind = InferSemanticKind(node),
578
+ Start = start,
579
+ End = end,
580
+ IsExported = 0,
581
+ ContainerKind = null,
582
+ ContainerRawKind = null,
583
+ ContainerRawKindSource = null,
584
+ ParamCount = null,
585
+ Metadata = null,
586
+ });
587
+ emittedKeys.Add(key);
588
+ }
589
+ }
590
+
591
+ private static void AddCommentTriviaNodes(CliParseResult result, CompilationUnitSyntax root)
592
+ {
593
+ var emittedKeys = result.Nodes
594
+ .Select(n => NodeKey(n.Start, n.End, n.RawKind ?? n.Kind))
595
+ .ToHashSet(StringComparer.Ordinal);
596
+ var rootIndex = result.Nodes.FindIndex(n =>
597
+ n.Start == NodeStart(root)
598
+ && n.End == NodeEnd(root)
599
+ && string.Equals(n.RawKind ?? n.Kind, root.Kind().ToString(), StringComparison.Ordinal));
600
+ var commentOrdinal = 0;
601
+
602
+ foreach (var trivia in root.DescendantTrivia(descendIntoTrivia: true))
603
+ {
604
+ if (!IsCommentTrivia(trivia)) { continue; }
605
+ var rawKind = trivia.Kind().ToString();
606
+ var start = trivia.Span.Start;
607
+ var end = trivia.Span.End;
608
+ var key = NodeKey(start, end, rawKind);
609
+ if (emittedKeys.Contains(key)) { continue; }
610
+ result.Nodes.Add(new CliNode
611
+ {
612
+ Name = null,
613
+ Kind = rawKind,
614
+ RawKind = rawKind,
615
+ RawKindSource = "roslyn-trivia",
616
+ SemanticKind = "comment",
617
+ Start = start,
618
+ End = end,
619
+ IsExported = 0,
620
+ ContainerKind = null,
621
+ ContainerRawKind = root.Kind().ToString(),
622
+ ContainerRawKindSource = "roslyn",
623
+ ParamCount = null,
624
+ ParentIndex = rootIndex >= 0 ? rootIndex : null,
625
+ Role = "comment",
626
+ IndexInParent = commentOrdinal,
627
+ Depth = rootIndex >= 0 ? 1 : 0,
628
+ Metadata = null,
629
+ });
630
+ emittedKeys.Add(key);
631
+ commentOrdinal++;
632
+ }
633
+ }
634
+
635
+ private static IEnumerable<SyntaxNode> EnumerateSyntaxNodesIncludingRoot(CompilationUnitSyntax root)
636
+ {
637
+ yield return root;
638
+ foreach (var node in root.DescendantNodes())
639
+ {
640
+ yield return node;
641
+ }
642
+ }
643
+
644
+ private static bool IsCommentTrivia(SyntaxTrivia trivia)
645
+ {
646
+ return trivia.IsKind(SyntaxKind.SingleLineCommentTrivia)
647
+ || trivia.IsKind(SyntaxKind.MultiLineCommentTrivia)
648
+ || trivia.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia)
649
+ || trivia.IsKind(SyntaxKind.MultiLineDocumentationCommentTrivia)
650
+ || trivia.IsKind(SyntaxKind.DocumentationCommentExteriorTrivia);
651
+ }
652
+
653
+ private static int NodeStart(SyntaxNode node)
654
+ {
655
+ return node is CompilationUnitSyntax ? node.FullSpan.Start : node.Span.Start;
656
+ }
657
+
658
+ private static int NodeEnd(SyntaxNode node)
659
+ {
660
+ return node is CompilationUnitSyntax ? node.FullSpan.End : node.Span.End;
661
+ }
662
+
538
663
  private static string NormalizeQualifiedName(string? qualifiedName)
539
664
  {
540
665
  if (string.IsNullOrWhiteSpace(qualifiedName)) { return string.Empty; }
@@ -1327,7 +1452,7 @@ namespace Pando.CSharpIndexer
1327
1452
  metadata["isStatic"] = node.Modifiers.Any(SyntaxKind.StaticKeyword);
1328
1453
  metadata["returnType"] = node.Type.ToString();
1329
1454
 
1330
- var kind = node.ImplicitOrExplicitKeyword.Kind() == SyntaxKind.ImplicitKeyword ? "implicit" : "explicit";
1455
+ var kind = node.ImplicitOrExplicitKeyword.IsKind(SyntaxKind.ImplicitKeyword) ? "implicit" : "explicit";
1331
1456
  result.Nodes.Add(new CliNode
1332
1457
  {
1333
1458
  Name = $"{kind} operator {node.Type}",
@@ -1436,12 +1561,21 @@ namespace Pando.CSharpIndexer
1436
1561
 
1437
1562
  private static void EnrichNodeHierarchy(List<CliNode> nodes, CompilationUnitSyntax root)
1438
1563
  {
1439
- var syntaxByKey = root.DescendantNodes()
1440
- .GroupBy(n => NodeKey(n.Span.Start, n.Span.End, n.Kind().ToString()))
1564
+ var syntaxByKey = EnumerateSyntaxNodesIncludingRoot(root)
1565
+ .GroupBy(n => NodeKey(NodeStart(n), NodeEnd(n), n.Kind().ToString()))
1441
1566
  .ToDictionary(g => g.Key, g => g.First(), StringComparer.Ordinal);
1442
1567
  var nodeByKey = nodes
1443
1568
  .GroupBy(n => NodeKey(n.Start, n.End, n.RawKind ?? n.Kind), StringComparer.Ordinal)
1444
1569
  .ToDictionary(g => g.Key, g => g.First(), StringComparer.Ordinal);
1570
+ var nodeIndexByKey = new Dictionary<string, int>(StringComparer.Ordinal);
1571
+ for (var i = 0; i < nodes.Count; i++)
1572
+ {
1573
+ var key = NodeKey(nodes[i].Start, nodes[i].End, nodes[i].RawKind ?? nodes[i].Kind);
1574
+ if (!nodeIndexByKey.ContainsKey(key))
1575
+ {
1576
+ nodeIndexByKey[key] = i;
1577
+ }
1578
+ }
1445
1579
  var emittedKeys = new HashSet<string>(nodeByKey.Keys, StringComparer.Ordinal);
1446
1580
  var childrenByParent = new Dictionary<string, List<CliNode>>(StringComparer.Ordinal);
1447
1581
 
@@ -1453,17 +1587,25 @@ namespace Pando.CSharpIndexer
1453
1587
  var body = GetBodySpan(syntax);
1454
1588
  if (body != null)
1455
1589
  {
1590
+ cliNode.BodyStart = body.Value.start;
1591
+ cliNode.BodyEnd = body.Value.end;
1456
1592
  metadata["bodyStart"] = body.Value.start;
1457
1593
  metadata["bodyEnd"] = body.Value.end;
1458
1594
  }
1459
- metadata["role"] = InferNodeRole(syntax);
1595
+ var role = InferNodeRole(syntax);
1596
+ cliNode.Role = role;
1597
+ metadata["role"] = role;
1460
1598
 
1461
1599
  var parent = syntax.Parent;
1462
1600
  while (parent != null)
1463
1601
  {
1464
- var parentKey = NodeKey(parent.Span.Start, parent.Span.End, parent.Kind().ToString());
1602
+ var parentKey = NodeKey(NodeStart(parent), NodeEnd(parent), parent.Kind().ToString());
1465
1603
  if (emittedKeys.Contains(parentKey))
1466
1604
  {
1605
+ if (nodeIndexByKey.TryGetValue(parentKey, out var parentIndex))
1606
+ {
1607
+ cliNode.ParentIndex = parentIndex;
1608
+ }
1467
1609
  metadata["parentStart"] = parent.Span.Start;
1468
1610
  metadata["parentEnd"] = parent.Span.End;
1469
1611
  metadata["parentKind"] = parent.Kind().ToString();
@@ -1501,6 +1643,7 @@ namespace Pando.CSharpIndexer
1501
1643
  for (var i = 0; i < ordered.Count; i++)
1502
1644
  {
1503
1645
  var metadata = ordered[i].Metadata ??= new Dictionary<string, object?>();
1646
+ ordered[i].IndexInParent = i;
1504
1647
  metadata["indexInParent"] = i;
1505
1648
  }
1506
1649
  }
@@ -1508,17 +1651,14 @@ namespace Pando.CSharpIndexer
1508
1651
  foreach (var cliNode in nodes)
1509
1652
  {
1510
1653
  var depth = 0;
1511
- var metadata = cliNode.Metadata;
1512
- while (metadata != null
1513
- && metadata.TryGetValue("parentStart", out var start)
1514
- && metadata.TryGetValue("parentEnd", out var end)
1515
- && metadata.TryGetValue("parentKind", out var kind))
1654
+ var parentIndex = cliNode.ParentIndex;
1655
+ var visited = new HashSet<int>();
1656
+ while (parentIndex != null && parentIndex >= 0 && parentIndex < nodes.Count && visited.Add(parentIndex.Value))
1516
1657
  {
1517
- var parentKey = NodeKey(Convert.ToInt32(start), Convert.ToInt32(end), Convert.ToString(kind) ?? "");
1518
- if (!nodeByKey.TryGetValue(parentKey, out var parent)) { break; }
1519
1658
  depth++;
1520
- metadata = parent.Metadata;
1659
+ parentIndex = nodes[parentIndex.Value].ParentIndex;
1521
1660
  }
1661
+ cliNode.Depth = depth;
1522
1662
  cliNode.Metadata ??= new Dictionary<string, object?>();
1523
1663
  cliNode.Metadata["depth"] = depth;
1524
1664
  }