pando-ai 0.1.4 → 0.1.9

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.
Files changed (150) hide show
  1. package/dist/cli.js +124 -124
  2. package/dist/watcher-process.js +421 -421
  3. package/dist/workers/chunk-Z7LW7C4Z.mjs +518 -0
  4. package/dist/workers/indexer-worker.mjs +1 -1
  5. package/dist/workers/snapshot-worker.mjs +8 -4
  6. package/package.json +5 -4
  7. package/resources/tools/{pando-tools.json → generated_pando-tools.json} +98 -4
  8. package/resources/tools/generated_tools.md +93 -0
  9. package/tools/clojure-editor/lib/pando-clojure-editor-standalone.jar +0 -0
  10. package/tools/clojure-indexer/lib/pando-clojure-indexer-standalone.jar +0 -0
  11. package/tools/csharp-indexer/CSharpIndexer.csproj +1 -1
  12. package/tools/csharp-indexer/bin/pando-csharp-indexer +81 -23
  13. package/tools/csharp-indexer/bin/pando-csharp-indexer.cmd +82 -14
  14. package/tools/csharp-indexer/src/Program.cs +1860 -0
  15. package/dist/workers/chunk-BD3AO7O3.mjs +0 -522
  16. package/tools/csharp-indexer/bin/Release/net10.0/CSharpIndexer +0 -0
  17. package/tools/csharp-indexer/bin/Release/net10.0/CSharpIndexer.deps.json +0 -383
  18. package/tools/csharp-indexer/bin/Release/net10.0/CSharpIndexer.dll +0 -0
  19. package/tools/csharp-indexer/bin/Release/net10.0/CSharpIndexer.pdb +0 -0
  20. package/tools/csharp-indexer/bin/Release/net10.0/CSharpIndexer.runtimeconfig.json +0 -13
  21. package/tools/csharp-indexer/bin/Release/net10.0/Humanizer.dll +0 -0
  22. package/tools/csharp-indexer/bin/Release/net10.0/Microsoft.CodeAnalysis.CSharp.Workspaces.dll +0 -0
  23. package/tools/csharp-indexer/bin/Release/net10.0/Microsoft.CodeAnalysis.CSharp.dll +0 -0
  24. package/tools/csharp-indexer/bin/Release/net10.0/Microsoft.CodeAnalysis.Workspaces.dll +0 -0
  25. package/tools/csharp-indexer/bin/Release/net10.0/Microsoft.CodeAnalysis.dll +0 -0
  26. package/tools/csharp-indexer/bin/Release/net10.0/System.Composition.AttributedModel.dll +0 -0
  27. package/tools/csharp-indexer/bin/Release/net10.0/System.Composition.Convention.dll +0 -0
  28. package/tools/csharp-indexer/bin/Release/net10.0/System.Composition.Hosting.dll +0 -0
  29. package/tools/csharp-indexer/bin/Release/net10.0/System.Composition.Runtime.dll +0 -0
  30. package/tools/csharp-indexer/bin/Release/net10.0/System.Composition.TypedParts.dll +0 -0
  31. package/tools/csharp-indexer/bin/Release/net10.0/cs/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  32. package/tools/csharp-indexer/bin/Release/net10.0/cs/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  33. package/tools/csharp-indexer/bin/Release/net10.0/cs/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  34. package/tools/csharp-indexer/bin/Release/net10.0/cs/Microsoft.CodeAnalysis.resources.dll +0 -0
  35. package/tools/csharp-indexer/bin/Release/net10.0/de/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  36. package/tools/csharp-indexer/bin/Release/net10.0/de/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  37. package/tools/csharp-indexer/bin/Release/net10.0/de/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  38. package/tools/csharp-indexer/bin/Release/net10.0/de/Microsoft.CodeAnalysis.resources.dll +0 -0
  39. package/tools/csharp-indexer/bin/Release/net10.0/es/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  40. package/tools/csharp-indexer/bin/Release/net10.0/es/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  41. package/tools/csharp-indexer/bin/Release/net10.0/es/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  42. package/tools/csharp-indexer/bin/Release/net10.0/es/Microsoft.CodeAnalysis.resources.dll +0 -0
  43. package/tools/csharp-indexer/bin/Release/net10.0/fr/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  44. package/tools/csharp-indexer/bin/Release/net10.0/fr/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  45. package/tools/csharp-indexer/bin/Release/net10.0/fr/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  46. package/tools/csharp-indexer/bin/Release/net10.0/fr/Microsoft.CodeAnalysis.resources.dll +0 -0
  47. package/tools/csharp-indexer/bin/Release/net10.0/it/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  48. package/tools/csharp-indexer/bin/Release/net10.0/it/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  49. package/tools/csharp-indexer/bin/Release/net10.0/it/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  50. package/tools/csharp-indexer/bin/Release/net10.0/it/Microsoft.CodeAnalysis.resources.dll +0 -0
  51. package/tools/csharp-indexer/bin/Release/net10.0/ja/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  52. package/tools/csharp-indexer/bin/Release/net10.0/ja/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  53. package/tools/csharp-indexer/bin/Release/net10.0/ja/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  54. package/tools/csharp-indexer/bin/Release/net10.0/ja/Microsoft.CodeAnalysis.resources.dll +0 -0
  55. package/tools/csharp-indexer/bin/Release/net10.0/ko/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  56. package/tools/csharp-indexer/bin/Release/net10.0/ko/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  57. package/tools/csharp-indexer/bin/Release/net10.0/ko/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  58. package/tools/csharp-indexer/bin/Release/net10.0/ko/Microsoft.CodeAnalysis.resources.dll +0 -0
  59. package/tools/csharp-indexer/bin/Release/net10.0/pl/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  60. package/tools/csharp-indexer/bin/Release/net10.0/pl/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  61. package/tools/csharp-indexer/bin/Release/net10.0/pl/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  62. package/tools/csharp-indexer/bin/Release/net10.0/pl/Microsoft.CodeAnalysis.resources.dll +0 -0
  63. package/tools/csharp-indexer/bin/Release/net10.0/pt-BR/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  64. package/tools/csharp-indexer/bin/Release/net10.0/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  65. package/tools/csharp-indexer/bin/Release/net10.0/pt-BR/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  66. package/tools/csharp-indexer/bin/Release/net10.0/pt-BR/Microsoft.CodeAnalysis.resources.dll +0 -0
  67. package/tools/csharp-indexer/bin/Release/net10.0/ru/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  68. package/tools/csharp-indexer/bin/Release/net10.0/ru/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  69. package/tools/csharp-indexer/bin/Release/net10.0/ru/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  70. package/tools/csharp-indexer/bin/Release/net10.0/ru/Microsoft.CodeAnalysis.resources.dll +0 -0
  71. package/tools/csharp-indexer/bin/Release/net10.0/tr/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  72. package/tools/csharp-indexer/bin/Release/net10.0/tr/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  73. package/tools/csharp-indexer/bin/Release/net10.0/tr/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  74. package/tools/csharp-indexer/bin/Release/net10.0/tr/Microsoft.CodeAnalysis.resources.dll +0 -0
  75. package/tools/csharp-indexer/bin/Release/net10.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  76. package/tools/csharp-indexer/bin/Release/net10.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  77. package/tools/csharp-indexer/bin/Release/net10.0/zh-Hans/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  78. package/tools/csharp-indexer/bin/Release/net10.0/zh-Hans/Microsoft.CodeAnalysis.resources.dll +0 -0
  79. package/tools/csharp-indexer/bin/Release/net10.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  80. package/tools/csharp-indexer/bin/Release/net10.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  81. package/tools/csharp-indexer/bin/Release/net10.0/zh-Hant/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  82. package/tools/csharp-indexer/bin/Release/net10.0/zh-Hant/Microsoft.CodeAnalysis.resources.dll +0 -0
  83. package/tools/csharp-indexer/bin/Release/net8.0/CSharpIndexer +0 -0
  84. package/tools/csharp-indexer/bin/Release/net8.0/CSharpIndexer.deps.json +0 -400
  85. package/tools/csharp-indexer/bin/Release/net8.0/CSharpIndexer.dll +0 -0
  86. package/tools/csharp-indexer/bin/Release/net8.0/CSharpIndexer.pdb +0 -0
  87. package/tools/csharp-indexer/bin/Release/net8.0/CSharpIndexer.runtimeconfig.json +0 -13
  88. package/tools/csharp-indexer/bin/Release/net8.0/Humanizer.dll +0 -0
  89. package/tools/csharp-indexer/bin/Release/net8.0/Microsoft.CodeAnalysis.CSharp.Workspaces.dll +0 -0
  90. package/tools/csharp-indexer/bin/Release/net8.0/Microsoft.CodeAnalysis.CSharp.dll +0 -0
  91. package/tools/csharp-indexer/bin/Release/net8.0/Microsoft.CodeAnalysis.Workspaces.dll +0 -0
  92. package/tools/csharp-indexer/bin/Release/net8.0/Microsoft.CodeAnalysis.dll +0 -0
  93. package/tools/csharp-indexer/bin/Release/net8.0/System.Composition.AttributedModel.dll +0 -0
  94. package/tools/csharp-indexer/bin/Release/net8.0/System.Composition.Convention.dll +0 -0
  95. package/tools/csharp-indexer/bin/Release/net8.0/System.Composition.Hosting.dll +0 -0
  96. package/tools/csharp-indexer/bin/Release/net8.0/System.Composition.Runtime.dll +0 -0
  97. package/tools/csharp-indexer/bin/Release/net8.0/System.Composition.TypedParts.dll +0 -0
  98. package/tools/csharp-indexer/bin/Release/net8.0/System.IO.Pipelines.dll +0 -0
  99. package/tools/csharp-indexer/bin/Release/net8.0/cs/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  100. package/tools/csharp-indexer/bin/Release/net8.0/cs/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  101. package/tools/csharp-indexer/bin/Release/net8.0/cs/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  102. package/tools/csharp-indexer/bin/Release/net8.0/cs/Microsoft.CodeAnalysis.resources.dll +0 -0
  103. package/tools/csharp-indexer/bin/Release/net8.0/de/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  104. package/tools/csharp-indexer/bin/Release/net8.0/de/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  105. package/tools/csharp-indexer/bin/Release/net8.0/de/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  106. package/tools/csharp-indexer/bin/Release/net8.0/de/Microsoft.CodeAnalysis.resources.dll +0 -0
  107. package/tools/csharp-indexer/bin/Release/net8.0/es/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  108. package/tools/csharp-indexer/bin/Release/net8.0/es/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  109. package/tools/csharp-indexer/bin/Release/net8.0/es/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  110. package/tools/csharp-indexer/bin/Release/net8.0/es/Microsoft.CodeAnalysis.resources.dll +0 -0
  111. package/tools/csharp-indexer/bin/Release/net8.0/fr/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  112. package/tools/csharp-indexer/bin/Release/net8.0/fr/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  113. package/tools/csharp-indexer/bin/Release/net8.0/fr/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  114. package/tools/csharp-indexer/bin/Release/net8.0/fr/Microsoft.CodeAnalysis.resources.dll +0 -0
  115. package/tools/csharp-indexer/bin/Release/net8.0/it/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  116. package/tools/csharp-indexer/bin/Release/net8.0/it/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  117. package/tools/csharp-indexer/bin/Release/net8.0/it/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  118. package/tools/csharp-indexer/bin/Release/net8.0/it/Microsoft.CodeAnalysis.resources.dll +0 -0
  119. package/tools/csharp-indexer/bin/Release/net8.0/ja/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  120. package/tools/csharp-indexer/bin/Release/net8.0/ja/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  121. package/tools/csharp-indexer/bin/Release/net8.0/ja/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  122. package/tools/csharp-indexer/bin/Release/net8.0/ja/Microsoft.CodeAnalysis.resources.dll +0 -0
  123. package/tools/csharp-indexer/bin/Release/net8.0/ko/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  124. package/tools/csharp-indexer/bin/Release/net8.0/ko/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  125. package/tools/csharp-indexer/bin/Release/net8.0/ko/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  126. package/tools/csharp-indexer/bin/Release/net8.0/ko/Microsoft.CodeAnalysis.resources.dll +0 -0
  127. package/tools/csharp-indexer/bin/Release/net8.0/pl/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  128. package/tools/csharp-indexer/bin/Release/net8.0/pl/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  129. package/tools/csharp-indexer/bin/Release/net8.0/pl/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  130. package/tools/csharp-indexer/bin/Release/net8.0/pl/Microsoft.CodeAnalysis.resources.dll +0 -0
  131. package/tools/csharp-indexer/bin/Release/net8.0/pt-BR/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  132. package/tools/csharp-indexer/bin/Release/net8.0/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  133. package/tools/csharp-indexer/bin/Release/net8.0/pt-BR/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  134. package/tools/csharp-indexer/bin/Release/net8.0/pt-BR/Microsoft.CodeAnalysis.resources.dll +0 -0
  135. package/tools/csharp-indexer/bin/Release/net8.0/ru/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  136. package/tools/csharp-indexer/bin/Release/net8.0/ru/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  137. package/tools/csharp-indexer/bin/Release/net8.0/ru/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  138. package/tools/csharp-indexer/bin/Release/net8.0/ru/Microsoft.CodeAnalysis.resources.dll +0 -0
  139. package/tools/csharp-indexer/bin/Release/net8.0/tr/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  140. package/tools/csharp-indexer/bin/Release/net8.0/tr/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  141. package/tools/csharp-indexer/bin/Release/net8.0/tr/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  142. package/tools/csharp-indexer/bin/Release/net8.0/tr/Microsoft.CodeAnalysis.resources.dll +0 -0
  143. package/tools/csharp-indexer/bin/Release/net8.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  144. package/tools/csharp-indexer/bin/Release/net8.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  145. package/tools/csharp-indexer/bin/Release/net8.0/zh-Hans/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  146. package/tools/csharp-indexer/bin/Release/net8.0/zh-Hans/Microsoft.CodeAnalysis.resources.dll +0 -0
  147. package/tools/csharp-indexer/bin/Release/net8.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
  148. package/tools/csharp-indexer/bin/Release/net8.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
  149. package/tools/csharp-indexer/bin/Release/net8.0/zh-Hant/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
  150. package/tools/csharp-indexer/bin/Release/net8.0/zh-Hant/Microsoft.CodeAnalysis.resources.dll +0 -0
@@ -0,0 +1,1860 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.IO;
4
+ using System.Linq;
5
+ using System.Text.Json;
6
+ using System.Text.Json.Serialization;
7
+ using System.Reflection;
8
+ using System.Threading;
9
+ using Microsoft.CodeAnalysis;
10
+ using Microsoft.CodeAnalysis.CSharp;
11
+ using Microsoft.CodeAnalysis.CSharp.Syntax;
12
+
13
+ namespace Pando.CSharpIndexer
14
+ {
15
+ public class CliNode
16
+ {
17
+ [JsonPropertyName("name")] public string? Name { get; set; }
18
+ [JsonPropertyName("kind")] public string Kind { get; set; } = "";
19
+ [JsonPropertyName("rawKind")] public string? RawKind { get; set; }
20
+ [JsonPropertyName("rawKindSource")] public string? RawKindSource { get; set; }
21
+ [JsonPropertyName("containerRawKind")] public string? ContainerRawKind { get; set; }
22
+ [JsonPropertyName("containerRawKindSource")] public string? ContainerRawKindSource { get; set; }
23
+ [JsonPropertyName("semanticKind")] public string? SemanticKind { get; set; }
24
+ [JsonPropertyName("start")] public int Start { get; set; }
25
+ [JsonPropertyName("end")] public int End { get; set; }
26
+ [JsonPropertyName("isExported")] public int IsExported { get; set; }
27
+ [JsonPropertyName("containerKind")] public string? ContainerKind { get; set; }
28
+ [JsonPropertyName("paramCount")] public int? ParamCount { get; set; }
29
+ [JsonPropertyName("qualifiedName")] public string? QualifiedName { get; set; }
30
+ [JsonPropertyName("scopeChain")] public List<string>? ScopeChain { get; set; }
31
+ [JsonPropertyName("symbolKey")] public string? SymbolKey { get; set; }
32
+ [JsonPropertyName("metadata")] public Dictionary<string, object?>? Metadata { get; set; }
33
+ }
34
+
35
+ public class CliImport
36
+ {
37
+ [JsonPropertyName("module")] public string? Module { get; set; }
38
+ [JsonPropertyName("isTypeOnly")] public int IsTypeOnly { get; set; }
39
+ }
40
+
41
+ public class CliImportBinding
42
+ {
43
+ [JsonPropertyName("module")] public string? Module { get; set; }
44
+ [JsonPropertyName("localName")] public string LocalName { get; set; } = "";
45
+ [JsonPropertyName("importedName")] public string ImportedName { get; set; } = "";
46
+ [JsonPropertyName("kind")] public string Kind { get; set; } = "named";
47
+ [JsonPropertyName("isTypeOnly")] public int IsTypeOnly { get; set; }
48
+ }
49
+
50
+ public class CliReference
51
+ {
52
+ [JsonPropertyName("sourceStart")] public int SourceStart { get; set; }
53
+ [JsonPropertyName("sourceEnd")] public int SourceEnd { get; set; }
54
+ [JsonPropertyName("refKind")] public string RefKind { get; set; } = "call";
55
+ [JsonPropertyName("refName")] public string? RefName { get; set; }
56
+ [JsonPropertyName("receiverName")] public string? ReceiverName { get; set; }
57
+ [JsonPropertyName("arity")] public int? Arity { get; set; }
58
+ [JsonPropertyName("containerQualifiedName")] public string? ContainerQualifiedName { get; set; }
59
+ [JsonPropertyName("targetSymbolKey")] public string? TargetSymbolKey { get; set; }
60
+ [JsonPropertyName("metadata")] public Dictionary<string, object?>? Metadata { get; set; }
61
+ }
62
+
63
+ public class CliSymbolMention
64
+ {
65
+ [JsonPropertyName("name")] public string? Name { get; set; }
66
+ [JsonPropertyName("mentionKind")] public string MentionKind { get; set; } = "reference";
67
+ [JsonPropertyName("targetSymbolKey")] public string? TargetSymbolKey { get; set; }
68
+ [JsonPropertyName("containerQualifiedName")] public string? ContainerQualifiedName { get; set; }
69
+ [JsonPropertyName("count")] public int Count { get; set; } = 1;
70
+ }
71
+
72
+ public class CliReferenceSpan
73
+ {
74
+ [JsonPropertyName("file")] public string File { get; set; } = "";
75
+ [JsonPropertyName("start")] public int Start { get; set; }
76
+ [JsonPropertyName("end")] public int End { get; set; }
77
+ [JsonPropertyName("refKind")] public string RefKind { get; set; } = "reference";
78
+ [JsonPropertyName("callerStart")] public int? CallerStart { get; set; }
79
+ [JsonPropertyName("callerEnd")] public int? CallerEnd { get; set; }
80
+ [JsonPropertyName("callerKind")] public string? CallerKind { get; set; }
81
+ // "statement" — the reference is the callee of an InvocationExpression that
82
+ // forms a complete ExpressionStatement; safe to delete the whole statement.
83
+ // "expression" — the reference participates in a larger expression; deletion
84
+ // would corrupt syntax. Callers should refuse and surface manual-review.
85
+ [JsonPropertyName("enclosingKind")] public string? EnclosingKind { get; set; }
86
+ [JsonPropertyName("enclosingStart")] public int? EnclosingStart { get; set; }
87
+ [JsonPropertyName("enclosingEnd")] public int? EnclosingEnd { get; set; }
88
+ }
89
+
90
+ public class CliParseResult
91
+ {
92
+ [JsonPropertyName("nodes")] public List<CliNode> Nodes { get; set; } = new();
93
+ [JsonPropertyName("imports")] public List<CliImport> Imports { get; set; } = new();
94
+ [JsonPropertyName("importBindings")] public List<CliImportBinding> ImportBindings { get; set; } = new();
95
+ [JsonPropertyName("references")] public List<CliReference> References { get; set; } = new();
96
+ [JsonPropertyName("symbolMentions")] public List<CliSymbolMention> SymbolMentions { get; set; } = new();
97
+ [JsonPropertyName("modulePublic")] public List<string> ModulePublic { get; set; } = new();
98
+ [JsonPropertyName("explicitExports")] public List<string>? ExplicitExports { get; set; }
99
+ [JsonPropertyName("error")] public string? Error { get; set; }
100
+ [JsonIgnore] internal Dictionary<string, CliSymbolMention>? SymbolMentionIndex { get; set; }
101
+ }
102
+
103
+ public class CliValidationResult
104
+ {
105
+ [JsonPropertyName("ok")] public bool Ok { get; set; } = true;
106
+ [JsonPropertyName("error")] public string? Error { get; set; }
107
+ }
108
+
109
+ public class CliResolvedSpan
110
+ {
111
+ [JsonPropertyName("start")] public int Start { get; set; }
112
+ [JsonPropertyName("end")] public int End { get; set; }
113
+ [JsonPropertyName("bodyStart")] public int? BodyStart { get; set; }
114
+ [JsonPropertyName("bodyEnd")] public int? BodyEnd { get; set; }
115
+ [JsonPropertyName("text")] public string? Text { get; set; }
116
+ [JsonPropertyName("error")] public string? Error { get; set; }
117
+ }
118
+
119
+ internal static class Program
120
+ {
121
+ private static int Main(string[] args)
122
+ {
123
+ try
124
+ {
125
+ var parsed = ParseArgs(args);
126
+ if (parsed.ContainsKey("server"))
127
+ {
128
+ return RunServer();
129
+ }
130
+ if (!parsed.TryGetValue("file", out var file) || string.IsNullOrWhiteSpace(file))
131
+ {
132
+ WriteError("missing --file");
133
+ return 2;
134
+ }
135
+
136
+ var content = File.ReadAllText(file!);
137
+ var tree = CSharpSyntaxTree.ParseText(content);
138
+ var root = tree.GetCompilationUnitRoot();
139
+
140
+ if (parsed.ContainsKey("validate"))
141
+ {
142
+ var validation = ValidateSyntax(tree);
143
+ WriteOutput(parsed, validation);
144
+ return validation.Error == null ? 0 : 2;
145
+ }
146
+
147
+ if (parsed.ContainsKey("resolve"))
148
+ {
149
+ if (!parsed.TryGetValue("nodePath", out var nodePath))
150
+ {
151
+ WriteError("missing --nodePath");
152
+ return 2;
153
+ }
154
+ var resolved = ResolveSpan(nodePath!, root, content);
155
+ WriteOutput(parsed, resolved);
156
+ return resolved.Error == null ? 0 : 2;
157
+ }
158
+
159
+ var result = BuildParseResult(root, content);
160
+ WriteOutput(parsed, result);
161
+ return 0;
162
+ }
163
+ catch (Exception ex)
164
+ {
165
+ WriteError(ex.Message);
166
+ return 2;
167
+ }
168
+ }
169
+
170
+ private static Dictionary<string, string?> ParseArgs(string[] args)
171
+ {
172
+ var map = new Dictionary<string, string?>(StringComparer.OrdinalIgnoreCase);
173
+ for (int i = 0; i < args.Length; i++)
174
+ {
175
+ var arg = args[i];
176
+ if (!arg.StartsWith("--", StringComparison.Ordinal)) { continue; }
177
+ var key = arg.Substring(2);
178
+ string? value = null;
179
+ if (i + 1 < args.Length && !args[i + 1].StartsWith("--", StringComparison.Ordinal))
180
+ {
181
+ value = args[i + 1];
182
+ i++;
183
+ }
184
+ map[key] = value;
185
+ }
186
+ return map;
187
+ }
188
+
189
+ private static int RunServer()
190
+ {
191
+ var options = new JsonSerializerOptions
192
+ {
193
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
194
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
195
+ };
196
+ string? line;
197
+ while ((line = Console.ReadLine()) != null)
198
+ {
199
+ if (string.IsNullOrWhiteSpace(line)) { continue; }
200
+ try
201
+ {
202
+ using var doc = JsonDocument.Parse(line);
203
+ var root = doc.RootElement;
204
+ var id = root.TryGetProperty("id", out var idEl) && idEl.TryGetInt64(out var idVal) ? idVal : 0;
205
+ var op = root.TryGetProperty("op", out var opEl) ? opEl.GetString() ?? "" : "";
206
+ if (string.Equals(op, "parse", StringComparison.OrdinalIgnoreCase))
207
+ {
208
+ if (root.TryGetProperty("files", out var filesEl) && filesEl.ValueKind == JsonValueKind.Array)
209
+ {
210
+ var results = new List<object>();
211
+ foreach (var item in filesEl.EnumerateArray())
212
+ {
213
+ var file = GetString(item, "file");
214
+ if (string.IsNullOrWhiteSpace(file))
215
+ {
216
+ results.Add(new { file, ok = false, error = "missing file" });
217
+ continue;
218
+ }
219
+ try
220
+ {
221
+ var result = ParseFile(file!);
222
+ results.Add(new { file, ok = true, result });
223
+ }
224
+ catch (Exception ex)
225
+ {
226
+ results.Add(new { file, ok = false, error = ex.Message });
227
+ }
228
+ }
229
+ WriteServerResponse(new { id, ok = true, results }, options);
230
+ continue;
231
+ }
232
+ var singleFile = GetString(root, "file");
233
+ if (string.IsNullOrWhiteSpace(singleFile))
234
+ {
235
+ WriteServerResponse(new { id, ok = false, error = "missing file" }, options);
236
+ continue;
237
+ }
238
+ try
239
+ {
240
+ var result = ParseFile(singleFile!);
241
+ WriteServerResponse(new { id, ok = true, result }, options);
242
+ }
243
+ catch (Exception ex)
244
+ {
245
+ WriteServerResponse(new { id, ok = false, error = ex.Message }, options);
246
+ }
247
+ continue;
248
+ }
249
+ if (string.Equals(op, "resolve", StringComparison.OrdinalIgnoreCase))
250
+ {
251
+ var file = GetString(root, "file");
252
+ var nodePath = GetString(root, "nodePath");
253
+ if (string.IsNullOrWhiteSpace(file) || string.IsNullOrWhiteSpace(nodePath))
254
+ {
255
+ WriteServerResponse(new { id, ok = false, error = "missing file or nodePath" }, options);
256
+ continue;
257
+ }
258
+ try
259
+ {
260
+ var resolved = ResolveFile(file!, nodePath!);
261
+ WriteServerResponse(new { id, ok = true, result = resolved }, options);
262
+ }
263
+ catch (Exception ex)
264
+ {
265
+ WriteServerResponse(new { id, ok = false, error = ex.Message }, options);
266
+ }
267
+ continue;
268
+ }
269
+ if (string.Equals(op, "resolve-references", StringComparison.OrdinalIgnoreCase))
270
+ {
271
+ var file = GetString(root, "file");
272
+ var nodePath = GetString(root, "nodePath");
273
+ var files = GetStringArray(root, "files");
274
+ var relTypes = GetStringArray(root, "relTypes");
275
+ var maxResults = GetInt(root, "maxResults");
276
+ var includeTargetFileReferences = GetBool(root, "includeTargetFileReferences") ?? true;
277
+ var aggregateByCaller = GetBool(root, "aggregateByCaller") ?? false;
278
+ if (string.IsNullOrWhiteSpace(file) || string.IsNullOrWhiteSpace(nodePath) || files == null || files.Count == 0)
279
+ {
280
+ WriteServerResponse(new { id, ok = false, error = "missing file, nodePath, or files" }, options);
281
+ continue;
282
+ }
283
+ try
284
+ {
285
+ var resolved = ResolveReferencesInFiles(file!, nodePath!, files, relTypes, maxResults, includeTargetFileReferences, aggregateByCaller);
286
+ WriteServerResponse(new { id, ok = true, result = resolved }, options);
287
+ }
288
+ catch (Exception ex)
289
+ {
290
+ WriteServerResponse(new { id, ok = false, error = ex.Message }, options);
291
+ }
292
+ continue;
293
+ }
294
+ WriteServerResponse(new { id, ok = false, error = "unknown op" }, options);
295
+ }
296
+ catch (Exception ex)
297
+ {
298
+ try
299
+ {
300
+ WriteServerResponse(new { id = 0, ok = false, error = ex.Message }, options);
301
+ }
302
+ catch { }
303
+ }
304
+ }
305
+ return 0;
306
+ }
307
+
308
+ private static string? GetString(JsonElement element, string name)
309
+ {
310
+ if (element.ValueKind == JsonValueKind.Object && element.TryGetProperty(name, out var prop))
311
+ {
312
+ if (prop.ValueKind == JsonValueKind.String) { return prop.GetString(); }
313
+ }
314
+ return null;
315
+ }
316
+
317
+ private static List<string>? GetStringArray(JsonElement element, string name)
318
+ {
319
+ if (element.ValueKind != JsonValueKind.Object) { return null; }
320
+ if (!element.TryGetProperty(name, out var prop) || prop.ValueKind != JsonValueKind.Array) { return null; }
321
+ var list = new List<string>();
322
+ foreach (var item in prop.EnumerateArray())
323
+ {
324
+ if (item.ValueKind == JsonValueKind.String)
325
+ {
326
+ var val = item.GetString();
327
+ if (!string.IsNullOrWhiteSpace(val)) { list.Add(val!); }
328
+ }
329
+ }
330
+ return list;
331
+ }
332
+
333
+ private static int? GetInt(JsonElement element, string name)
334
+ {
335
+ if (element.ValueKind != JsonValueKind.Object) { return null; }
336
+ if (!element.TryGetProperty(name, out var prop)) { return null; }
337
+ if (prop.ValueKind == JsonValueKind.Number && prop.TryGetInt32(out var value)) { return value; }
338
+ return null;
339
+ }
340
+
341
+ private static bool? GetBool(JsonElement element, string name)
342
+ {
343
+ if (element.ValueKind != JsonValueKind.Object) { return null; }
344
+ if (!element.TryGetProperty(name, out var prop)) { return null; }
345
+ if (prop.ValueKind == JsonValueKind.True) { return true; }
346
+ if (prop.ValueKind == JsonValueKind.False) { return false; }
347
+ return null;
348
+ }
349
+
350
+ private static CliParseResult ParseFile(string file)
351
+ {
352
+ var content = File.ReadAllText(file);
353
+ var tree = CSharpSyntaxTree.ParseText(content, path: file);
354
+ var root = tree.GetCompilationUnitRoot();
355
+ return BuildParseResult(root, content);
356
+ }
357
+
358
+ private static CliResolvedSpan ResolveFile(string file, string nodePath)
359
+ {
360
+ var content = File.ReadAllText(file);
361
+ var tree = CSharpSyntaxTree.ParseText(content, path: file);
362
+ var root = tree.GetCompilationUnitRoot();
363
+ return ResolveSpan(nodePath, root, content);
364
+ }
365
+
366
+ private static CliValidationResult ValidateSyntax(SyntaxTree tree)
367
+ {
368
+ var errors = tree.GetDiagnostics().Where(d => d.Severity == DiagnosticSeverity.Error).ToList();
369
+ if (errors.Count == 0)
370
+ {
371
+ return new CliValidationResult { Ok = true };
372
+ }
373
+ var msg = string.Join("; ", errors.Take(5).Select(e => e.GetMessage()));
374
+ return new CliValidationResult { Ok = false, Error = $"{errors.Count} error(s): {msg}" };
375
+ }
376
+
377
+ private static void WriteServerResponse(object payload, JsonSerializerOptions options)
378
+ {
379
+ Console.WriteLine(JsonSerializer.Serialize(payload, options));
380
+ }
381
+
382
+ private static void WriteOutput(Dictionary<string, string?> args, object payload)
383
+ {
384
+ var json = JsonSerializer.Serialize(payload, new JsonSerializerOptions
385
+ {
386
+ DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
387
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
388
+ });
389
+ if (args.TryGetValue("output", out var output) && !string.IsNullOrWhiteSpace(output))
390
+ {
391
+ File.WriteAllText(output!, json);
392
+ }
393
+ else
394
+ {
395
+ Console.WriteLine(json);
396
+ }
397
+ }
398
+
399
+ private static void WriteError(string message)
400
+ {
401
+ Console.WriteLine(JsonSerializer.Serialize(new CliParseResult { Error = message }));
402
+ }
403
+
404
+ private static SemanticModel? TryCreateSemanticModel(CompilationUnitSyntax root)
405
+ {
406
+ try
407
+ {
408
+ var compilation = CreateCompilation(new[] { root.SyntaxTree });
409
+ return compilation.GetSemanticModel(root.SyntaxTree, ignoreAccessibility: true);
410
+ }
411
+ catch
412
+ {
413
+ return null;
414
+ }
415
+ }
416
+
417
+ private static CliParseResult BuildParseResult(CompilationUnitSyntax root, string content)
418
+ {
419
+ var result = new CliParseResult();
420
+ var semanticModel = TryCreateSemanticModel(root);
421
+
422
+ foreach (var u in root.Usings)
423
+ {
424
+ if (u.Name == null) { continue; }
425
+ var module = u.Name.ToString();
426
+ result.Imports.Add(new CliImport { Module = module, IsTypeOnly = 0 });
427
+ if (u.Alias != null)
428
+ {
429
+ result.ImportBindings.Add(new CliImportBinding
430
+ {
431
+ Module = module,
432
+ LocalName = u.Alias.Name.ToString(),
433
+ ImportedName = module,
434
+ Kind = "named",
435
+ IsTypeOnly = 0,
436
+ });
437
+ }
438
+ }
439
+
440
+ var publicNames = new HashSet<string>(StringComparer.Ordinal);
441
+
442
+ foreach (var node in root.DescendantNodes())
443
+ {
444
+ switch (node)
445
+ {
446
+ case EnumDeclarationSyntax enumDecl:
447
+ AddEnumNode(result, enumDecl, content, publicNames, semanticModel);
448
+ break;
449
+ case BaseTypeDeclarationSyntax typeDecl:
450
+ AddTypeNode(result, typeDecl, content, publicNames, semanticModel);
451
+ break;
452
+ case MethodDeclarationSyntax method:
453
+ AddMethodNode(result, method, content, semanticModel);
454
+ break;
455
+ case ConstructorDeclarationSyntax ctor:
456
+ AddConstructorNode(result, ctor, content, semanticModel);
457
+ break;
458
+ case PropertyDeclarationSyntax prop:
459
+ AddPropertyNode(result, prop, content, semanticModel);
460
+ break;
461
+ case IndexerDeclarationSyntax indexer:
462
+ AddIndexerNode(result, indexer, content, semanticModel);
463
+ break;
464
+ case FieldDeclarationSyntax field:
465
+ AddFieldNodes(result, field, content, semanticModel);
466
+ break;
467
+ case EventDeclarationSyntax ev:
468
+ AddEventNode(result, ev, content, semanticModel);
469
+ break;
470
+ case EventFieldDeclarationSyntax evField:
471
+ AddEventFieldNodes(result, evField, content, semanticModel);
472
+ break;
473
+ case DelegateDeclarationSyntax del:
474
+ AddDelegateNode(result, del, content, publicNames, semanticModel);
475
+ break;
476
+ case EnumMemberDeclarationSyntax enumMember:
477
+ AddEnumMemberNode(result, enumMember, semanticModel);
478
+ break;
479
+ case IfStatementSyntax ifStmt:
480
+ AddStructuralNode(result, ifStmt, semanticModel);
481
+ break;
482
+ case ForStatementSyntax forStmt:
483
+ AddStructuralNode(result, forStmt, semanticModel);
484
+ break;
485
+ case ForEachStatementSyntax forEachStmt:
486
+ AddStructuralNode(result, forEachStmt, semanticModel);
487
+ break;
488
+ case ForEachVariableStatementSyntax forEachVarStmt:
489
+ AddStructuralNode(result, forEachVarStmt, semanticModel);
490
+ break;
491
+ case SwitchStatementSyntax switchStmt:
492
+ AddStructuralNode(result, switchStmt, semanticModel);
493
+ break;
494
+ case ReturnStatementSyntax returnStmt:
495
+ AddStructuralNode(result, returnStmt, semanticModel);
496
+ break;
497
+ case ExpressionStatementSyntax exprStmt:
498
+ AddStructuralNode(result, exprStmt, semanticModel);
499
+ break;
500
+ case InvocationExpressionSyntax call:
501
+ AddStructuralNode(result, call, semanticModel);
502
+ AddCallReference(result, call, content, semanticModel);
503
+ break;
504
+ case ObjectCreationExpressionSyntax created:
505
+ AddStructuralNode(result, created, semanticModel);
506
+ AddNewReference(result, created, content, semanticModel);
507
+ break;
508
+ case IdentifierNameSyntax idName:
509
+ AddIdentifierReference(result, idName, content, semanticModel);
510
+ break;
511
+ case GenericNameSyntax genName:
512
+ AddIdentifierReference(result, genName, content, semanticModel);
513
+ break;
514
+ case VariableDeclaratorSyntax localVar when ShouldIndexLocalVariable(localVar):
515
+ AddStructuralNode(result, localVar, semanticModel);
516
+ break;
517
+ case OperatorDeclarationSyntax op:
518
+ AddOperatorNode(result, op, content, semanticModel);
519
+ break;
520
+ case ConversionOperatorDeclarationSyntax conv:
521
+ AddConversionOperatorNode(result, conv, content, semanticModel);
522
+ break;
523
+ case DestructorDeclarationSyntax dtor:
524
+ AddDestructorNode(result, dtor, content, semanticModel);
525
+ break;
526
+ case LocalFunctionStatementSyntax localFn:
527
+ AddLocalFunctionNode(result, localFn, content, semanticModel);
528
+ break;
529
+ }
530
+ }
531
+
532
+ EnrichNodeHierarchy(result.Nodes, root);
533
+
534
+ result.ModulePublic = publicNames.ToList();
535
+ return result;
536
+ }
537
+
538
+ private static string NormalizeQualifiedName(string? qualifiedName)
539
+ {
540
+ if (string.IsNullOrWhiteSpace(qualifiedName)) { return string.Empty; }
541
+ return qualifiedName.StartsWith("global::", StringComparison.Ordinal)
542
+ ? qualifiedName.Substring("global::".Length)
543
+ : qualifiedName;
544
+ }
545
+
546
+ private static List<string> BuildScopeChain(ISymbol symbol)
547
+ {
548
+ var chain = new List<string>();
549
+ ISymbol? current = symbol;
550
+ while (current != null)
551
+ {
552
+ if (current is INamespaceSymbol ns)
553
+ {
554
+ if (!ns.IsGlobalNamespace && !string.IsNullOrWhiteSpace(ns.Name))
555
+ {
556
+ chain.Add(ns.Name);
557
+ }
558
+ }
559
+ else if (!string.IsNullOrWhiteSpace(current.Name))
560
+ {
561
+ chain.Add(current.Name);
562
+ }
563
+ current = current.ContainingSymbol;
564
+ }
565
+ chain.Reverse();
566
+ return chain;
567
+ }
568
+
569
+ private static ISymbol? TryGetSymbol(SemanticModel? model, SyntaxNode node, bool declared = true)
570
+ {
571
+ if (model == null) { return null; }
572
+ try
573
+ {
574
+ ISymbol? symbol = declared ? model.GetDeclaredSymbol(node) : null;
575
+ if (symbol == null)
576
+ {
577
+ var info = model.GetSymbolInfo(node);
578
+ symbol = info.Symbol ?? info.CandidateSymbols.FirstOrDefault();
579
+ }
580
+ if (symbol == null && !declared)
581
+ {
582
+ switch (node)
583
+ {
584
+ case InvocationExpressionSyntax invocation:
585
+ {
586
+ var info = model.GetSymbolInfo(invocation.Expression);
587
+ symbol = info.Symbol ?? info.CandidateSymbols.FirstOrDefault();
588
+ break;
589
+ }
590
+ case ObjectCreationExpressionSyntax creation:
591
+ {
592
+ var info = model.GetSymbolInfo(creation.Type);
593
+ symbol = info.Symbol ?? info.CandidateSymbols.FirstOrDefault();
594
+ break;
595
+ }
596
+ }
597
+ }
598
+ return symbol;
599
+ }
600
+ catch
601
+ {
602
+ return null;
603
+ }
604
+ }
605
+
606
+ private static void PopulateNodeSymbolInfo(CliNode node, SemanticModel? model, SyntaxNode syntaxNode)
607
+ {
608
+ var symbol = TryGetSymbol(model, syntaxNode, declared: true);
609
+ if (symbol == null) { return; }
610
+
611
+ var qualified = NormalizeQualifiedName(symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
612
+ if (!string.IsNullOrWhiteSpace(qualified))
613
+ {
614
+ node.QualifiedName = qualified;
615
+ }
616
+ var chain = BuildScopeChain(symbol);
617
+ if (chain.Count > 0)
618
+ {
619
+ node.ScopeChain = chain;
620
+ }
621
+ var key = TryGetSymbolKey(symbol);
622
+ if (!string.IsNullOrWhiteSpace(key))
623
+ {
624
+ node.SymbolKey = key;
625
+ }
626
+ }
627
+
628
+ private static void AddSymbolMention(
629
+ CliParseResult result,
630
+ string? name,
631
+ string mentionKind,
632
+ string? targetSymbolKey,
633
+ string? containerQualifiedName)
634
+ {
635
+ var cleanedName = string.IsNullOrWhiteSpace(name) ? null : name.Trim();
636
+ if (string.IsNullOrWhiteSpace(cleanedName)) { return; }
637
+ var cleanedKind = string.IsNullOrWhiteSpace(mentionKind) ? "reference" : mentionKind.Trim().ToLowerInvariant();
638
+ var cleanedTargetKey = string.IsNullOrWhiteSpace(targetSymbolKey) ? string.Empty : targetSymbolKey.Trim();
639
+ var cleanedContainer = string.IsNullOrWhiteSpace(containerQualifiedName) ? string.Empty : containerQualifiedName.Trim();
640
+ result.SymbolMentionIndex ??= new Dictionary<string, CliSymbolMention>(StringComparer.Ordinal);
641
+ var dedupeKey = $"{cleanedName}\u0000{cleanedKind}\u0000{cleanedTargetKey}\u0000{cleanedContainer}";
642
+ if (result.SymbolMentionIndex.TryGetValue(dedupeKey, out var existing))
643
+ {
644
+ existing.Count += 1;
645
+ return;
646
+ }
647
+ var mention = new CliSymbolMention
648
+ {
649
+ Name = cleanedName,
650
+ MentionKind = cleanedKind,
651
+ TargetSymbolKey = string.IsNullOrWhiteSpace(cleanedTargetKey) ? null : cleanedTargetKey,
652
+ ContainerQualifiedName = string.IsNullOrWhiteSpace(cleanedContainer) ? null : cleanedContainer,
653
+ Count = 1,
654
+ };
655
+ result.SymbolMentionIndex[dedupeKey] = mention;
656
+ result.SymbolMentions.Add(mention);
657
+ }
658
+
659
+ // Stable, cross-file identifier for a Roslyn symbol. Uses the public
660
+ // documentation comment ID (e.g. "T:Newtonsoft.Json.Linq.JValue" or
661
+ // "M:Foo.Bar(System.Int32)"), which is unique and reproducible across
662
+ // independent compilations as long as the fully qualified name matches.
663
+ // Falls back to fully qualified display string when no doc comment id
664
+ // is produced (e.g. anonymous/local symbols).
665
+ private static string? TryGetSymbolKey(ISymbol symbol)
666
+ {
667
+ try
668
+ {
669
+ var docId = symbol.GetDocumentationCommentId();
670
+ if (!string.IsNullOrWhiteSpace(docId) && docId != "!:") { return docId; }
671
+ var fq = symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
672
+ if (string.IsNullOrWhiteSpace(fq)) { return null; }
673
+ return $"FQ:{fq}";
674
+ }
675
+ catch
676
+ {
677
+ return null;
678
+ }
679
+ }
680
+
681
+ private static CSharpCompilation CreateCompilation(IEnumerable<SyntaxTree> trees)
682
+ {
683
+ var refs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
684
+ void AddRef(Type t)
685
+ {
686
+ try
687
+ {
688
+ var loc = t.Assembly.Location;
689
+ if (!string.IsNullOrWhiteSpace(loc))
690
+ {
691
+ refs.Add(loc);
692
+ }
693
+ }
694
+ catch { }
695
+ }
696
+
697
+ AddRef(typeof(object));
698
+ AddRef(typeof(Enumerable));
699
+ AddRef(typeof(Console));
700
+ AddRef(typeof(System.Runtime.GCSettings));
701
+
702
+ var metadata = refs.Select(r => MetadataReference.CreateFromFile(r)).ToList();
703
+ return CSharpCompilation.Create(
704
+ "PandoIndex",
705
+ trees,
706
+ metadata,
707
+ new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
708
+ }
709
+
710
+ private static object ResolveReferencesInFiles(string targetFile, string nodePath, List<string> files, List<string>? relTypes = null, int? maxResults = null, bool includeTargetFileReferences = true, bool aggregateByCaller = false)
711
+ {
712
+ var targetRef = ParseNodePathRef(nodePath);
713
+ if (targetRef == null) { return new { error = "invalid nodePath" }; }
714
+
715
+ var targetContent = File.ReadAllText(targetFile);
716
+ var targetTree = CSharpSyntaxTree.ParseText(targetContent, path: targetFile);
717
+ var targetRoot = targetTree.GetCompilationUnitRoot();
718
+ var targetNode = FindNodeByRef(targetRoot, targetRef.Value.start, targetRef.Value.end, targetRef.Value.kind);
719
+ if (targetNode == null) { return new { error = "target node not found" }; }
720
+
721
+ var trees = new List<SyntaxTree> { targetTree };
722
+ var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { targetFile };
723
+ foreach (var file in files.OrderBy(static f => f, StringComparer.OrdinalIgnoreCase))
724
+ {
725
+ if (string.IsNullOrWhiteSpace(file)) { continue; }
726
+ var normalized = file;
727
+ if (seen.Contains(normalized)) { continue; }
728
+ seen.Add(normalized);
729
+ try
730
+ {
731
+ var text = File.ReadAllText(normalized);
732
+ trees.Add(CSharpSyntaxTree.ParseText(text, path: normalized));
733
+ }
734
+ catch { }
735
+ }
736
+
737
+ var compilation = CreateCompilation(trees);
738
+ var targetModel = compilation.GetSemanticModel(targetTree, ignoreAccessibility: true);
739
+ var targetSymbol = TryGetSymbol(targetModel, targetNode, declared: true) ?? TryGetSymbol(targetModel, targetNode, declared: false);
740
+ if (targetSymbol == null) { return new { error = "target symbol not found" }; }
741
+ var targetKey = TryGetSymbolKey(targetSymbol);
742
+ if (string.IsNullOrWhiteSpace(targetKey)) { return new { error = "target symbol key missing" }; }
743
+
744
+ var references = new List<CliReferenceSpan>();
745
+ var allowedRelTypes = relTypes != null && relTypes.Count > 0
746
+ ? new HashSet<string>(relTypes.Where(static value => !string.IsNullOrWhiteSpace(value)), StringComparer.OrdinalIgnoreCase)
747
+ : null;
748
+ var cappedResults = maxResults.HasValue && maxResults.Value > 0 ? maxResults.Value : int.MaxValue;
749
+ var truncated = false;
750
+ var seenCallerGroups = aggregateByCaller ? new HashSet<string>(StringComparer.Ordinal) : null;
751
+ foreach (var tree in trees)
752
+ {
753
+ var root = tree.GetCompilationUnitRoot();
754
+ var model = compilation.GetSemanticModel(tree, ignoreAccessibility: true);
755
+ var filePath = tree.FilePath;
756
+ if (string.IsNullOrWhiteSpace(filePath)) { continue; }
757
+ if (!includeTargetFileReferences && string.Equals(filePath, targetFile, StringComparison.OrdinalIgnoreCase)) { continue; }
758
+
759
+ var seenSpans = new HashSet<(int, int)>();
760
+ foreach (var node in root.DescendantNodes())
761
+ {
762
+ ISymbol? sym = null;
763
+ int spanStart = node.Span.Start;
764
+ int spanEnd = node.Span.End;
765
+ if (node is InvocationExpressionSyntax || node is ObjectCreationExpressionSyntax)
766
+ {
767
+ sym = TryGetSymbol(model, node, declared: false);
768
+ }
769
+ else if (node is SimpleNameSyntax simpleName)
770
+ {
771
+ // Skip identifiers that are the immediate callee of an
772
+ // InvocationExpression or the type of an ObjectCreation —
773
+ // those nodes are already emitted with a wider span and
774
+ // would otherwise produce duplicate references.
775
+ if (IsRedundantWithCallOrNew(simpleName)) { continue; }
776
+ sym = TryGetSymbol(model, node, declared: false);
777
+ spanStart = simpleName.Identifier.Span.Start;
778
+ spanEnd = simpleName.Identifier.Span.End;
779
+ }
780
+ if (sym == null) { continue; }
781
+ var key = TryGetSymbolKey(sym);
782
+ if (string.IsNullOrWhiteSpace(key) || key != targetKey) { continue; }
783
+ if (!seenSpans.Add((spanStart, spanEnd))) { continue; }
784
+ var refKind = ClassifyResolvedReferenceKind(node, sym);
785
+ if (allowedRelTypes != null && !allowedRelTypes.Contains(refKind)) { continue; }
786
+ var caller = FindEnclosingCallerDeclaration(node);
787
+ var (enclosingKind, enclosingStart, enclosingEnd) = ClassifyReferenceContext(node);
788
+ references.Add(new CliReferenceSpan
789
+ {
790
+ File = filePath,
791
+ Start = spanStart,
792
+ End = spanEnd,
793
+ RefKind = refKind,
794
+ CallerStart = caller.HasValue ? caller.Value.start : null,
795
+ CallerEnd = caller.HasValue ? caller.Value.end : null,
796
+ CallerKind = caller.HasValue ? caller.Value.kind : null,
797
+ EnclosingKind = enclosingKind,
798
+ EnclosingStart = enclosingStart,
799
+ EnclosingEnd = enclosingEnd,
800
+ });
801
+ if (aggregateByCaller)
802
+ {
803
+ var callerKey = caller != null
804
+ ? $"{filePath}\0{caller.Value.start}\0{caller.Value.end}\0{caller.Value.kind}"
805
+ : $"{filePath}\0{spanStart}\0{spanEnd}\0REF";
806
+ seenCallerGroups!.Add(callerKey);
807
+ if (seenCallerGroups.Count >= cappedResults)
808
+ {
809
+ truncated = true;
810
+ return new { symbolKey = targetKey, references, truncated };
811
+ }
812
+ }
813
+ else if (references.Count >= cappedResults)
814
+ {
815
+ truncated = true;
816
+ return new { symbolKey = targetKey, references, truncated };
817
+ }
818
+ }
819
+ }
820
+
821
+ return new { symbolKey = targetKey, references, truncated };
822
+ }
823
+
824
+ private static string ClassifyResolvedReferenceKind(SyntaxNode node, ISymbol symbol)
825
+ {
826
+ if (node is InvocationExpressionSyntax) { return "call"; }
827
+ if (node is ObjectCreationExpressionSyntax) { return "new"; }
828
+ return symbol.Kind switch
829
+ {
830
+ SymbolKind.NamedType => "type",
831
+ SymbolKind.Namespace => "type",
832
+ _ => "reference",
833
+ };
834
+ }
835
+
836
+ private static (int start, int end, string kind)? FindEnclosingCallerDeclaration(SyntaxNode node)
837
+ {
838
+ for (SyntaxNode? current = node; current != null; current = current.Parent)
839
+ {
840
+ switch (current)
841
+ {
842
+ case MethodDeclarationSyntax method:
843
+ return (method.Span.Start, method.Span.End, method.Kind().ToString());
844
+ case ConstructorDeclarationSyntax ctor:
845
+ return (ctor.Span.Start, ctor.Span.End, ctor.Kind().ToString());
846
+ case LocalFunctionStatementSyntax localFunction:
847
+ return (localFunction.Span.Start, localFunction.Span.End, localFunction.Kind().ToString());
848
+ case AccessorDeclarationSyntax accessor:
849
+ return (accessor.Span.Start, accessor.Span.End, accessor.Kind().ToString());
850
+ case ParenthesizedLambdaExpressionSyntax lambda:
851
+ return (lambda.Span.Start, lambda.Span.End, lambda.Kind().ToString());
852
+ case SimpleLambdaExpressionSyntax simpleLambda:
853
+ return (simpleLambda.Span.Start, simpleLambda.Span.End, simpleLambda.Kind().ToString());
854
+ case AnonymousMethodExpressionSyntax anonymousMethod:
855
+ return (anonymousMethod.Span.Start, anonymousMethod.Span.End, anonymousMethod.Kind().ToString());
856
+ }
857
+ }
858
+ return null;
859
+ }
860
+
861
+ // Walk up from a matched reference node to determine whether deleting it is
862
+ // syntactically safe. A reference whose enclosing chain ends at an
863
+ // ExpressionStatementSyntax can be removed by replacing the whole statement.
864
+ // Anything else (assignments, arguments, member-access chains, conditionals,
865
+ // string interpolation, etc.) is reported as "expression" so the caller can
866
+ // refuse and surface manual review instead of writing a corrupting marker.
867
+ private static (string kind, int? start, int? end) ClassifyReferenceContext(SyntaxNode refNode)
868
+ {
869
+ // Walk up through the chain that "wraps" this reference. Stop when we
870
+ // reach the outermost expression that *contains* the ref; if its parent
871
+ // is an ExpressionStatement, deletion is safe.
872
+ SyntaxNode current = refNode;
873
+ while (current.Parent is ExpressionSyntax parentExpr)
874
+ {
875
+ // Stop walking if the parent is unrelated (e.g., a sibling of an
876
+ // argument list). The chain we care about: identifier → member access
877
+ // → invocation. Anything beyond that (e.g., parent of an invocation
878
+ // that is itself the operand of a binary expression) is an expression
879
+ // context and not safe to delete wholesale.
880
+ if (parentExpr is MemberAccessExpressionSyntax memberAccess && memberAccess.Name == current)
881
+ {
882
+ current = parentExpr;
883
+ continue;
884
+ }
885
+ if (parentExpr is InvocationExpressionSyntax invocation && invocation.Expression == current)
886
+ {
887
+ current = parentExpr;
888
+ continue;
889
+ }
890
+ if (parentExpr is ObjectCreationExpressionSyntax objectCreation && objectCreation.Type == current)
891
+ {
892
+ current = parentExpr;
893
+ continue;
894
+ }
895
+ break;
896
+ }
897
+ if (current.Parent is ExpressionStatementSyntax stmt)
898
+ {
899
+ return ("statement", stmt.Span.Start, stmt.Span.End);
900
+ }
901
+ return ("expression", null, null);
902
+ }
903
+
904
+ private static string? GetContainerQualifiedName(SemanticModel? model, SyntaxNode syntaxNode)
905
+ {
906
+ if (model == null) { return null; }
907
+ try
908
+ {
909
+ var symbol = model.GetEnclosingSymbol(syntaxNode.SpanStart);
910
+ if (symbol == null) { return null; }
911
+ var qualified = NormalizeQualifiedName(symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
912
+ return string.IsNullOrWhiteSpace(qualified) ? null : qualified;
913
+ }
914
+ catch
915
+ {
916
+ return null;
917
+ }
918
+ }
919
+
920
+ private static void AddTypeNode(CliParseResult result, BaseTypeDeclarationSyntax node, string content, HashSet<string> publicNames, SemanticModel? model)
921
+ {
922
+ var kind = node switch
923
+ {
924
+ ClassDeclarationSyntax => "Class",
925
+ StructDeclarationSyntax => "Struct",
926
+ InterfaceDeclarationSyntax => "Interface",
927
+ RecordDeclarationSyntax record => record.ClassOrStructKeyword.IsKind(SyntaxKind.StructKeyword) ? "Struct" : "Class",
928
+ _ => "Class",
929
+ };
930
+ var semanticKind = kind switch
931
+ {
932
+ "Class" => "class",
933
+ "Struct" => "struct",
934
+ "Interface" => "interface",
935
+ _ => "class",
936
+ };
937
+ var modifiers = node.Modifiers;
938
+ var isPublic = modifiers.Any(SyntaxKind.PublicKeyword);
939
+ if (isPublic) { publicNames.Add(node.Identifier.Text); }
940
+
941
+ var metadata = BuildVisibilityMetadata(modifiers);
942
+ if (node is RecordDeclarationSyntax)
943
+ {
944
+ metadata["isRecord"] = true;
945
+ }
946
+ if (node is TypeDeclarationSyntax t && t.BaseList != null)
947
+ {
948
+ var bases = t.BaseList.Types.Select(tn => tn.Type.ToString()).ToList();
949
+ if (bases.Count > 0)
950
+ {
951
+ metadata["extends"] = bases.First();
952
+ if (bases.Count > 1)
953
+ {
954
+ metadata["implements"] = bases.Skip(1).ToList();
955
+ }
956
+ }
957
+ }
958
+
959
+ result.Nodes.Add(new CliNode
960
+ {
961
+ Name = node.Identifier.Text,
962
+ Kind = kind,
963
+ RawKind = node.Kind().ToString(),
964
+ RawKindSource = "roslyn",
965
+ SemanticKind = semanticKind,
966
+ Start = node.Span.Start,
967
+ End = node.Span.End,
968
+ IsExported = isPublic ? 1 : 0,
969
+ ContainerKind = null,
970
+ ContainerRawKind = null,
971
+ ContainerRawKindSource = null,
972
+ ParamCount = null,
973
+ Metadata = metadata,
974
+ });
975
+ PopulateNodeSymbolInfo(result.Nodes[^1], model, node);
976
+ }
977
+
978
+ private static void AddEnumNode(CliParseResult result, EnumDeclarationSyntax node, string content, HashSet<string> publicNames, SemanticModel? model)
979
+ {
980
+ var modifiers = node.Modifiers;
981
+ var isPublic = modifiers.Any(SyntaxKind.PublicKeyword);
982
+ if (isPublic) { publicNames.Add(node.Identifier.Text); }
983
+ var metadata = BuildVisibilityMetadata(modifiers);
984
+ result.Nodes.Add(new CliNode
985
+ {
986
+ Name = node.Identifier.Text,
987
+ Kind = "Enum",
988
+ RawKind = node.Kind().ToString(),
989
+ RawKindSource = "roslyn",
990
+ SemanticKind = "enum",
991
+ Start = node.Span.Start,
992
+ End = node.Span.End,
993
+ IsExported = isPublic ? 1 : 0,
994
+ ContainerKind = null,
995
+ ContainerRawKind = null,
996
+ ContainerRawKindSource = null,
997
+ ParamCount = null,
998
+ Metadata = metadata,
999
+ });
1000
+ PopulateNodeSymbolInfo(result.Nodes[^1], model, node);
1001
+ }
1002
+
1003
+ private static void AddDelegateNode(CliParseResult result, DelegateDeclarationSyntax node, string content, HashSet<string> publicNames, SemanticModel? model)
1004
+ {
1005
+ var modifiers = node.Modifiers;
1006
+ var isPublic = modifiers.Any(SyntaxKind.PublicKeyword);
1007
+ if (isPublic) { publicNames.Add(node.Identifier.Text); }
1008
+ var metadata = BuildVisibilityMetadata(modifiers);
1009
+ result.Nodes.Add(new CliNode
1010
+ {
1011
+ Name = node.Identifier.Text,
1012
+ Kind = "Delegate",
1013
+ RawKind = node.Kind().ToString(),
1014
+ RawKindSource = "roslyn",
1015
+ SemanticKind = "type",
1016
+ Start = node.Span.Start,
1017
+ End = node.Span.End,
1018
+ IsExported = isPublic ? 1 : 0,
1019
+ ContainerKind = null,
1020
+ ContainerRawKind = null,
1021
+ ContainerRawKindSource = null,
1022
+ ParamCount = node.ParameterList.Parameters.Count,
1023
+ Metadata = metadata,
1024
+ });
1025
+ PopulateNodeSymbolInfo(result.Nodes[^1], model, node);
1026
+ }
1027
+
1028
+ private static void AddMethodNode(CliParseResult result, MethodDeclarationSyntax node, string content, SemanticModel? model)
1029
+ {
1030
+ var container = node.Ancestors().OfType<BaseTypeDeclarationSyntax>().FirstOrDefault();
1031
+ var metadata = BuildVisibilityMetadata(node.Modifiers, container?.Identifier.Text, container?.Kind().ToString());
1032
+ metadata["isAsync"] = node.Modifiers.Any(SyntaxKind.AsyncKeyword);
1033
+ metadata["isStatic"] = node.Modifiers.Any(SyntaxKind.StaticKeyword);
1034
+ metadata["isAbstract"] = node.Modifiers.Any(SyntaxKind.AbstractKeyword);
1035
+ metadata["returnType"] = node.ReturnType.ToString();
1036
+
1037
+ result.Nodes.Add(new CliNode
1038
+ {
1039
+ Name = node.Identifier.Text,
1040
+ Kind = "Method",
1041
+ RawKind = node.Kind().ToString(),
1042
+ RawKindSource = "roslyn",
1043
+ ContainerRawKind = container?.Kind().ToString(),
1044
+ ContainerRawKindSource = container == null ? null : "roslyn",
1045
+ SemanticKind = "method",
1046
+ Start = node.Span.Start,
1047
+ End = node.Span.End,
1048
+ IsExported = 0,
1049
+ ContainerKind = container == null ? null : "Class",
1050
+ ParamCount = node.ParameterList.Parameters.Count,
1051
+ Metadata = metadata,
1052
+ });
1053
+ PopulateNodeSymbolInfo(result.Nodes[^1], model, node);
1054
+ }
1055
+
1056
+ private static void AddConstructorNode(CliParseResult result, ConstructorDeclarationSyntax node, string content, SemanticModel? model)
1057
+ {
1058
+ var container = node.Ancestors().OfType<BaseTypeDeclarationSyntax>().FirstOrDefault();
1059
+ var metadata = BuildVisibilityMetadata(node.Modifiers, container?.Identifier.Text, container?.Kind().ToString());
1060
+ metadata["isStatic"] = node.Modifiers.Any(SyntaxKind.StaticKeyword);
1061
+ metadata["isAbstract"] = node.Modifiers.Any(SyntaxKind.AbstractKeyword);
1062
+
1063
+ result.Nodes.Add(new CliNode
1064
+ {
1065
+ Name = node.Identifier.Text,
1066
+ Kind = "Method",
1067
+ RawKind = node.Kind().ToString(),
1068
+ RawKindSource = "roslyn",
1069
+ ContainerRawKind = container?.Kind().ToString(),
1070
+ ContainerRawKindSource = container == null ? null : "roslyn",
1071
+ SemanticKind = "method",
1072
+ Start = node.Span.Start,
1073
+ End = node.Span.End,
1074
+ IsExported = 0,
1075
+ ContainerKind = container == null ? null : "Class",
1076
+ ParamCount = node.ParameterList.Parameters.Count,
1077
+ Metadata = metadata,
1078
+ });
1079
+ PopulateNodeSymbolInfo(result.Nodes[^1], model, node);
1080
+ }
1081
+
1082
+ private static void AddPropertyNode(CliParseResult result, PropertyDeclarationSyntax node, string content, SemanticModel? model)
1083
+ {
1084
+ var container = node.Ancestors().OfType<BaseTypeDeclarationSyntax>().FirstOrDefault();
1085
+ var metadata = BuildVisibilityMetadata(node.Modifiers, container?.Identifier.Text, container?.Kind().ToString());
1086
+ metadata["isStatic"] = node.Modifiers.Any(SyntaxKind.StaticKeyword);
1087
+ metadata["isReadonly"] = node.AccessorList?.Accessors.All(a => a.Kind() == SyntaxKind.GetAccessorDeclaration) == true;
1088
+
1089
+ result.Nodes.Add(new CliNode
1090
+ {
1091
+ Name = node.Identifier.Text,
1092
+ Kind = "Property",
1093
+ RawKind = node.Kind().ToString(),
1094
+ RawKindSource = "roslyn",
1095
+ ContainerRawKind = container?.Kind().ToString(),
1096
+ ContainerRawKindSource = container == null ? null : "roslyn",
1097
+ SemanticKind = "property",
1098
+ Start = node.Span.Start,
1099
+ End = node.Span.End,
1100
+ IsExported = 0,
1101
+ ContainerKind = container == null ? null : "Class",
1102
+ ParamCount = null,
1103
+ Metadata = metadata,
1104
+ });
1105
+ PopulateNodeSymbolInfo(result.Nodes[^1], model, node);
1106
+ }
1107
+
1108
+ private static void AddFieldNodes(CliParseResult result, FieldDeclarationSyntax node, string content, SemanticModel? model)
1109
+ {
1110
+ var container = node.Ancestors().OfType<BaseTypeDeclarationSyntax>().FirstOrDefault();
1111
+ var metadata = BuildVisibilityMetadata(node.Modifiers, container?.Identifier.Text, container?.Kind().ToString());
1112
+ metadata["isStatic"] = node.Modifiers.Any(SyntaxKind.StaticKeyword);
1113
+ metadata["isReadonly"] = node.Modifiers.Any(SyntaxKind.ReadOnlyKeyword);
1114
+
1115
+ foreach (var v in node.Declaration.Variables)
1116
+ {
1117
+ result.Nodes.Add(new CliNode
1118
+ {
1119
+ Name = v.Identifier.Text,
1120
+ Kind = "Field",
1121
+ RawKind = v.Kind().ToString(),
1122
+ RawKindSource = "roslyn",
1123
+ ContainerRawKind = container?.Kind().ToString(),
1124
+ ContainerRawKindSource = container == null ? null : "roslyn",
1125
+ SemanticKind = "field",
1126
+ Start = v.Span.Start,
1127
+ End = v.Span.End,
1128
+ IsExported = 0,
1129
+ ContainerKind = container == null ? null : "Class",
1130
+ ParamCount = null,
1131
+ Metadata = metadata,
1132
+ });
1133
+ PopulateNodeSymbolInfo(result.Nodes[^1], model, v);
1134
+ }
1135
+ }
1136
+
1137
+ private static bool ShouldIndexLocalVariable(VariableDeclaratorSyntax node)
1138
+ {
1139
+ return node.Ancestors().Any(a =>
1140
+ a is BaseMethodDeclarationSyntax
1141
+ || a is LocalFunctionStatementSyntax
1142
+ || a is AccessorDeclarationSyntax
1143
+ || a is AnonymousFunctionExpressionSyntax)
1144
+ && !node.Ancestors().Any(a => a is FieldDeclarationSyntax || a is EventFieldDeclarationSyntax);
1145
+ }
1146
+
1147
+ private static void AddStructuralNode(CliParseResult result, SyntaxNode node, SemanticModel? model)
1148
+ {
1149
+ var container = node.Ancestors().FirstOrDefault(a =>
1150
+ a is BaseMethodDeclarationSyntax
1151
+ || a is LocalFunctionStatementSyntax
1152
+ || a is AccessorDeclarationSyntax
1153
+ || a is BaseTypeDeclarationSyntax);
1154
+ var containerKind = container switch
1155
+ {
1156
+ MethodDeclarationSyntax => "Method",
1157
+ ConstructorDeclarationSyntax => "Method",
1158
+ DestructorDeclarationSyntax => "Method",
1159
+ OperatorDeclarationSyntax => "Method",
1160
+ ConversionOperatorDeclarationSyntax => "Method",
1161
+ LocalFunctionStatementSyntax => "Function",
1162
+ AccessorDeclarationSyntax => "Property",
1163
+ BaseTypeDeclarationSyntax => "Class",
1164
+ _ => null,
1165
+ };
1166
+ var name = node switch
1167
+ {
1168
+ VariableDeclaratorSyntax v => v.Identifier.Text,
1169
+ ForEachStatementSyntax f => f.Identifier.Text,
1170
+ InvocationExpressionSyntax i => ExtractName(i.Expression),
1171
+ ObjectCreationExpressionSyntax o => ExtractName(o.Type),
1172
+ _ => null,
1173
+ };
1174
+
1175
+ result.Nodes.Add(new CliNode
1176
+ {
1177
+ Name = name,
1178
+ Kind = node.Kind().ToString(),
1179
+ RawKind = node.Kind().ToString(),
1180
+ RawKindSource = "roslyn",
1181
+ SemanticKind = null,
1182
+ Start = node.Span.Start,
1183
+ End = node.Span.End,
1184
+ IsExported = 0,
1185
+ ContainerKind = containerKind,
1186
+ ContainerRawKind = container?.Kind().ToString(),
1187
+ ContainerRawKindSource = container == null ? null : "roslyn",
1188
+ ParamCount = null,
1189
+ Metadata = null,
1190
+ });
1191
+ PopulateNodeSymbolInfo(result.Nodes[^1], model, node);
1192
+ }
1193
+
1194
+ private static void AddIndexerNode(CliParseResult result, IndexerDeclarationSyntax node, string content, SemanticModel? model)
1195
+ {
1196
+ var container = node.Ancestors().OfType<BaseTypeDeclarationSyntax>().FirstOrDefault();
1197
+ var metadata = BuildVisibilityMetadata(node.Modifiers, container?.Identifier.Text, container?.Kind().ToString());
1198
+ metadata["isStatic"] = node.Modifiers.Any(SyntaxKind.StaticKeyword);
1199
+ metadata["isReadonly"] = node.AccessorList?.Accessors.All(a => a.Kind() == SyntaxKind.GetAccessorDeclaration) == true;
1200
+
1201
+ result.Nodes.Add(new CliNode
1202
+ {
1203
+ Name = "this",
1204
+ Kind = "Property",
1205
+ RawKind = node.Kind().ToString(),
1206
+ RawKindSource = "roslyn",
1207
+ ContainerRawKind = container?.Kind().ToString(),
1208
+ ContainerRawKindSource = container == null ? null : "roslyn",
1209
+ SemanticKind = "property",
1210
+ Start = node.Span.Start,
1211
+ End = node.Span.End,
1212
+ IsExported = 0,
1213
+ ContainerKind = container == null ? null : "Class",
1214
+ ParamCount = node.ParameterList?.Parameters.Count ?? 0,
1215
+ Metadata = metadata,
1216
+ });
1217
+ PopulateNodeSymbolInfo(result.Nodes[^1], model, node);
1218
+ }
1219
+
1220
+ private static void AddEventNode(CliParseResult result, EventDeclarationSyntax node, string content, SemanticModel? model)
1221
+ {
1222
+ var container = node.Ancestors().OfType<BaseTypeDeclarationSyntax>().FirstOrDefault();
1223
+ var metadata = BuildVisibilityMetadata(node.Modifiers, container?.Identifier.Text, container?.Kind().ToString());
1224
+ metadata["isStatic"] = node.Modifiers.Any(SyntaxKind.StaticKeyword);
1225
+ metadata["isEvent"] = true;
1226
+
1227
+ result.Nodes.Add(new CliNode
1228
+ {
1229
+ Name = node.Identifier.Text,
1230
+ Kind = "Event",
1231
+ RawKind = node.Kind().ToString(),
1232
+ RawKindSource = "roslyn",
1233
+ ContainerRawKind = container?.Kind().ToString(),
1234
+ ContainerRawKindSource = container == null ? null : "roslyn",
1235
+ SemanticKind = "field",
1236
+ Start = node.Span.Start,
1237
+ End = node.Span.End,
1238
+ IsExported = 0,
1239
+ ContainerKind = container == null ? null : "Class",
1240
+ ParamCount = null,
1241
+ Metadata = metadata,
1242
+ });
1243
+ PopulateNodeSymbolInfo(result.Nodes[^1], model, node);
1244
+ }
1245
+
1246
+ private static void AddEventFieldNodes(CliParseResult result, EventFieldDeclarationSyntax node, string content, SemanticModel? model)
1247
+ {
1248
+ var container = node.Ancestors().OfType<BaseTypeDeclarationSyntax>().FirstOrDefault();
1249
+ var metadata = BuildVisibilityMetadata(node.Modifiers, container?.Identifier.Text, container?.Kind().ToString());
1250
+ metadata["isStatic"] = node.Modifiers.Any(SyntaxKind.StaticKeyword);
1251
+ metadata["isEvent"] = true;
1252
+
1253
+ foreach (var v in node.Declaration.Variables)
1254
+ {
1255
+ result.Nodes.Add(new CliNode
1256
+ {
1257
+ Name = v.Identifier.Text,
1258
+ Kind = "Event",
1259
+ RawKind = v.Kind().ToString(),
1260
+ RawKindSource = "roslyn",
1261
+ ContainerRawKind = container?.Kind().ToString(),
1262
+ ContainerRawKindSource = container == null ? null : "roslyn",
1263
+ SemanticKind = "field",
1264
+ Start = v.Span.Start,
1265
+ End = v.Span.End,
1266
+ IsExported = 0,
1267
+ ContainerKind = container == null ? null : "Class",
1268
+ ParamCount = null,
1269
+ Metadata = metadata,
1270
+ });
1271
+ PopulateNodeSymbolInfo(result.Nodes[^1], model, v);
1272
+ }
1273
+ }
1274
+
1275
+ private static void AddEnumMemberNode(CliParseResult result, EnumMemberDeclarationSyntax node, SemanticModel? model)
1276
+ {
1277
+ var container = node.Ancestors().OfType<EnumDeclarationSyntax>().FirstOrDefault();
1278
+ result.Nodes.Add(new CliNode
1279
+ {
1280
+ Name = node.Identifier.Text,
1281
+ Kind = "EnumMember",
1282
+ RawKind = node.Kind().ToString(),
1283
+ RawKindSource = "roslyn",
1284
+ SemanticKind = "enum",
1285
+ Start = node.Span.Start,
1286
+ End = node.Span.End,
1287
+ IsExported = 0,
1288
+ ContainerKind = container == null ? null : "Enum",
1289
+ ContainerRawKind = container?.Kind().ToString(),
1290
+ ContainerRawKindSource = container == null ? null : "roslyn",
1291
+ ParamCount = null,
1292
+ Metadata = null,
1293
+ });
1294
+ PopulateNodeSymbolInfo(result.Nodes[^1], model, node);
1295
+ }
1296
+
1297
+ private static void AddOperatorNode(CliParseResult result, OperatorDeclarationSyntax node, string content, SemanticModel? model)
1298
+ {
1299
+ var container = node.Ancestors().OfType<BaseTypeDeclarationSyntax>().FirstOrDefault();
1300
+ var metadata = BuildVisibilityMetadata(node.Modifiers, container?.Identifier.Text, container?.Kind().ToString());
1301
+ metadata["isStatic"] = node.Modifiers.Any(SyntaxKind.StaticKeyword);
1302
+ metadata["returnType"] = node.ReturnType.ToString();
1303
+
1304
+ result.Nodes.Add(new CliNode
1305
+ {
1306
+ Name = $"operator {node.OperatorToken.Text}",
1307
+ Kind = "Method",
1308
+ RawKind = node.Kind().ToString(),
1309
+ RawKindSource = "roslyn",
1310
+ ContainerRawKind = container?.Kind().ToString(),
1311
+ ContainerRawKindSource = container == null ? null : "roslyn",
1312
+ SemanticKind = "method",
1313
+ Start = node.Span.Start,
1314
+ End = node.Span.End,
1315
+ IsExported = 0,
1316
+ ContainerKind = container == null ? null : "Class",
1317
+ ParamCount = node.ParameterList.Parameters.Count,
1318
+ Metadata = metadata,
1319
+ });
1320
+ PopulateNodeSymbolInfo(result.Nodes[^1], model, node);
1321
+ }
1322
+
1323
+ private static void AddConversionOperatorNode(CliParseResult result, ConversionOperatorDeclarationSyntax node, string content, SemanticModel? model)
1324
+ {
1325
+ var container = node.Ancestors().OfType<BaseTypeDeclarationSyntax>().FirstOrDefault();
1326
+ var metadata = BuildVisibilityMetadata(node.Modifiers, container?.Identifier.Text, container?.Kind().ToString());
1327
+ metadata["isStatic"] = node.Modifiers.Any(SyntaxKind.StaticKeyword);
1328
+ metadata["returnType"] = node.Type.ToString();
1329
+
1330
+ var kind = node.ImplicitOrExplicitKeyword.Kind() == SyntaxKind.ImplicitKeyword ? "implicit" : "explicit";
1331
+ result.Nodes.Add(new CliNode
1332
+ {
1333
+ Name = $"{kind} operator {node.Type}",
1334
+ Kind = "Method",
1335
+ RawKind = node.Kind().ToString(),
1336
+ RawKindSource = "roslyn",
1337
+ ContainerRawKind = container?.Kind().ToString(),
1338
+ ContainerRawKindSource = container == null ? null : "roslyn",
1339
+ SemanticKind = "method",
1340
+ Start = node.Span.Start,
1341
+ End = node.Span.End,
1342
+ IsExported = 0,
1343
+ ContainerKind = container == null ? null : "Class",
1344
+ ParamCount = node.ParameterList.Parameters.Count,
1345
+ Metadata = metadata,
1346
+ });
1347
+ PopulateNodeSymbolInfo(result.Nodes[^1], model, node);
1348
+ }
1349
+
1350
+ private static void AddDestructorNode(CliParseResult result, DestructorDeclarationSyntax node, string content, SemanticModel? model)
1351
+ {
1352
+ var container = node.Ancestors().OfType<BaseTypeDeclarationSyntax>().FirstOrDefault();
1353
+ var metadata = BuildVisibilityMetadata(node.Modifiers, container?.Identifier.Text, container?.Kind().ToString());
1354
+
1355
+ result.Nodes.Add(new CliNode
1356
+ {
1357
+ Name = $"~{node.Identifier.Text}",
1358
+ Kind = "Method",
1359
+ RawKind = node.Kind().ToString(),
1360
+ RawKindSource = "roslyn",
1361
+ ContainerRawKind = container?.Kind().ToString(),
1362
+ ContainerRawKindSource = container == null ? null : "roslyn",
1363
+ SemanticKind = "method",
1364
+ Start = node.Span.Start,
1365
+ End = node.Span.End,
1366
+ IsExported = 0,
1367
+ ContainerKind = container == null ? null : "Class",
1368
+ ParamCount = 0,
1369
+ Metadata = metadata,
1370
+ });
1371
+ PopulateNodeSymbolInfo(result.Nodes[^1], model, node);
1372
+ }
1373
+
1374
+ private static void AddLocalFunctionNode(CliParseResult result, LocalFunctionStatementSyntax node, string content, SemanticModel? model)
1375
+ {
1376
+ var container = node.Ancestors().OfType<BaseTypeDeclarationSyntax>().FirstOrDefault();
1377
+ var metadata = BuildVisibilityMetadata(node.Modifiers, container?.Identifier.Text, container?.Kind().ToString());
1378
+ metadata["isLocal"] = true;
1379
+ metadata["returnType"] = node.ReturnType.ToString();
1380
+
1381
+ result.Nodes.Add(new CliNode
1382
+ {
1383
+ Name = node.Identifier.Text,
1384
+ Kind = "Function",
1385
+ RawKind = node.Kind().ToString(),
1386
+ RawKindSource = "roslyn",
1387
+ ContainerRawKind = container?.Kind().ToString(),
1388
+ ContainerRawKindSource = container == null ? null : "roslyn",
1389
+ SemanticKind = "function",
1390
+ Start = node.Span.Start,
1391
+ End = node.Span.End,
1392
+ IsExported = 0,
1393
+ ContainerKind = container == null ? null : "Class",
1394
+ ParamCount = node.ParameterList.Parameters.Count,
1395
+ Metadata = metadata,
1396
+ });
1397
+ PopulateNodeSymbolInfo(result.Nodes[^1], model, node);
1398
+ }
1399
+
1400
+ private static void AddStructuralNode(CliParseResult result, SyntaxNode node, string? name = null)
1401
+ {
1402
+ var metadata = new Dictionary<string, object?>();
1403
+ if (node is VariableDeclaratorSyntax variable)
1404
+ {
1405
+ metadata["hasInitializer"] = variable.Initializer != null;
1406
+ }
1407
+ var kind = node.Kind().ToString();
1408
+ result.Nodes.Add(new CliNode
1409
+ {
1410
+ Name = string.IsNullOrWhiteSpace(name) ? null : name,
1411
+ Kind = kind,
1412
+ RawKind = kind,
1413
+ RawKindSource = "roslyn",
1414
+ SemanticKind = InferSemanticKind(node),
1415
+ Start = node.Span.Start,
1416
+ End = node.Span.End,
1417
+ IsExported = 0,
1418
+ ContainerKind = null,
1419
+ ContainerRawKind = null,
1420
+ ContainerRawKindSource = null,
1421
+ ParamCount = null,
1422
+ Metadata = metadata,
1423
+ });
1424
+ }
1425
+
1426
+ private static string? InferSemanticKind(SyntaxNode node)
1427
+ {
1428
+ return node switch
1429
+ {
1430
+ StatementSyntax => "statement",
1431
+ ExpressionSyntax => "expression",
1432
+ VariableDeclaratorSyntax => "variable",
1433
+ _ => null,
1434
+ };
1435
+ }
1436
+
1437
+ private static void EnrichNodeHierarchy(List<CliNode> nodes, CompilationUnitSyntax root)
1438
+ {
1439
+ var syntaxByKey = root.DescendantNodes()
1440
+ .GroupBy(n => NodeKey(n.Span.Start, n.Span.End, n.Kind().ToString()))
1441
+ .ToDictionary(g => g.Key, g => g.First(), StringComparer.Ordinal);
1442
+ var nodeByKey = nodes
1443
+ .GroupBy(n => NodeKey(n.Start, n.End, n.RawKind ?? n.Kind), StringComparer.Ordinal)
1444
+ .ToDictionary(g => g.Key, g => g.First(), StringComparer.Ordinal);
1445
+ var emittedKeys = new HashSet<string>(nodeByKey.Keys, StringComparer.Ordinal);
1446
+ var childrenByParent = new Dictionary<string, List<CliNode>>(StringComparer.Ordinal);
1447
+
1448
+ foreach (var cliNode in nodes)
1449
+ {
1450
+ var key = NodeKey(cliNode.Start, cliNode.End, cliNode.RawKind ?? cliNode.Kind);
1451
+ if (!syntaxByKey.TryGetValue(key, out var syntax)) { continue; }
1452
+ var metadata = cliNode.Metadata ??= new Dictionary<string, object?>();
1453
+ var body = GetBodySpan(syntax);
1454
+ if (body != null)
1455
+ {
1456
+ metadata["bodyStart"] = body.Value.start;
1457
+ metadata["bodyEnd"] = body.Value.end;
1458
+ }
1459
+ metadata["role"] = InferNodeRole(syntax);
1460
+
1461
+ var parent = syntax.Parent;
1462
+ while (parent != null)
1463
+ {
1464
+ var parentKey = NodeKey(parent.Span.Start, parent.Span.End, parent.Kind().ToString());
1465
+ if (emittedKeys.Contains(parentKey))
1466
+ {
1467
+ metadata["parentStart"] = parent.Span.Start;
1468
+ metadata["parentEnd"] = parent.Span.End;
1469
+ metadata["parentKind"] = parent.Kind().ToString();
1470
+ if (!childrenByParent.TryGetValue(parentKey, out var list))
1471
+ {
1472
+ list = new List<CliNode>();
1473
+ childrenByParent[parentKey] = list;
1474
+ }
1475
+ list.Add(cliNode);
1476
+ break;
1477
+ }
1478
+ parent = parent.Parent;
1479
+ }
1480
+ }
1481
+
1482
+ foreach (var entry in childrenByParent)
1483
+ {
1484
+ var ordered = entry.Value
1485
+ .OrderBy(n => n.Start)
1486
+ .ThenBy(n => n.End)
1487
+ .ThenBy(n => n.RawKind ?? n.Kind)
1488
+ .ToList();
1489
+ if (nodeByKey.TryGetValue(entry.Key, out var parent))
1490
+ {
1491
+ var metadata = parent.Metadata ??= new Dictionary<string, object?>();
1492
+ metadata["children"] = ordered
1493
+ .Select(n => new Dictionary<string, object?>
1494
+ {
1495
+ ["start"] = n.Start,
1496
+ ["end"] = n.End,
1497
+ ["kind"] = n.RawKind ?? n.Kind,
1498
+ })
1499
+ .ToList();
1500
+ }
1501
+ for (var i = 0; i < ordered.Count; i++)
1502
+ {
1503
+ var metadata = ordered[i].Metadata ??= new Dictionary<string, object?>();
1504
+ metadata["indexInParent"] = i;
1505
+ }
1506
+ }
1507
+
1508
+ foreach (var cliNode in nodes)
1509
+ {
1510
+ 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))
1516
+ {
1517
+ var parentKey = NodeKey(Convert.ToInt32(start), Convert.ToInt32(end), Convert.ToString(kind) ?? "");
1518
+ if (!nodeByKey.TryGetValue(parentKey, out var parent)) { break; }
1519
+ depth++;
1520
+ metadata = parent.Metadata;
1521
+ }
1522
+ cliNode.Metadata ??= new Dictionary<string, object?>();
1523
+ cliNode.Metadata["depth"] = depth;
1524
+ }
1525
+ }
1526
+
1527
+ private static string NodeKey(int start, int end, string kind) => $"{start}:{end}:{kind}";
1528
+
1529
+ private static (int start, int end)? GetBodySpan(SyntaxNode node)
1530
+ {
1531
+ return node switch
1532
+ {
1533
+ BaseTypeDeclarationSyntax typeDecl when typeDecl.OpenBraceToken.Span.End <= typeDecl.CloseBraceToken.SpanStart
1534
+ => (typeDecl.OpenBraceToken.Span.End, typeDecl.CloseBraceToken.SpanStart),
1535
+ BaseMethodDeclarationSyntax method when method.Body != null
1536
+ => (method.Body.OpenBraceToken.Span.End, method.Body.CloseBraceToken.SpanStart),
1537
+ AccessorDeclarationSyntax accessor when accessor.Body != null
1538
+ => (accessor.Body.OpenBraceToken.Span.End, accessor.Body.CloseBraceToken.SpanStart),
1539
+ AnonymousFunctionExpressionSyntax anon when anon.Body is BlockSyntax block
1540
+ => (block.OpenBraceToken.Span.End, block.CloseBraceToken.SpanStart),
1541
+ _ => null,
1542
+ };
1543
+ }
1544
+
1545
+ private static string InferNodeRole(SyntaxNode node)
1546
+ {
1547
+ var parent = node.Parent;
1548
+ if (parent == null) { return "root"; }
1549
+ if (parent is BaseTypeDeclarationSyntax) { return "member"; }
1550
+ if (parent is BlockSyntax) { return "statement"; }
1551
+ if (parent is IfStatementSyntax ifStatement)
1552
+ {
1553
+ if (ifStatement.Condition == node) { return "condition"; }
1554
+ if (ifStatement.Statement == node) { return "then"; }
1555
+ if (ifStatement.Else?.Statement == node) { return "else"; }
1556
+ }
1557
+ if (parent is ForStatementSyntax forStatement)
1558
+ {
1559
+ if (forStatement.Condition == node) { return "condition"; }
1560
+ if (forStatement.Statement == node) { return "body"; }
1561
+ if (forStatement.Declaration == node) { return "initializer"; }
1562
+ }
1563
+ if (parent is ForEachStatementSyntax forEachStatement && forEachStatement.Statement == node) { return "body"; }
1564
+ if (parent is ForEachVariableStatementSyntax forEachVariableStatement && forEachVariableStatement.Statement == node) { return "body"; }
1565
+ if (parent is SwitchStatementSyntax switchStatement)
1566
+ {
1567
+ if (switchStatement.Expression == node) { return "expression"; }
1568
+ if (switchStatement.Sections.Contains(node)) { return "section"; }
1569
+ }
1570
+ if (parent is ReturnStatementSyntax returnStatement && returnStatement.Expression == node) { return "value"; }
1571
+ if (parent is InvocationExpressionSyntax invocation)
1572
+ {
1573
+ if (invocation.Expression == node) { return "callee"; }
1574
+ if (invocation.ArgumentList.Arguments.Contains(node)) { return "argument"; }
1575
+ }
1576
+ if (parent is ObjectCreationExpressionSyntax creation)
1577
+ {
1578
+ if (creation.Type == node) { return "type"; }
1579
+ if (creation.ArgumentList?.Arguments.Contains(node) == true) { return "argument"; }
1580
+ }
1581
+ if (parent is VariableDeclarationSyntax) { return "variable"; }
1582
+ if (parent is EqualsValueClauseSyntax) { return "initializer"; }
1583
+ if (parent is ExpressionStatementSyntax) { return "expression"; }
1584
+ if (node is StatementSyntax) { return "statement"; }
1585
+ if (node is ExpressionSyntax) { return "expression"; }
1586
+ return "child";
1587
+ }
1588
+
1589
+ private static void AddCallReference(CliParseResult result, InvocationExpressionSyntax node, string content, SemanticModel? model)
1590
+ {
1591
+ var name = ExtractName(node.Expression);
1592
+ var containerQualifiedName = GetContainerQualifiedName(model, node);
1593
+ var symbol = TryGetSymbol(model, node, declared: false);
1594
+ var targetSymbolKey = symbol == null ? null : TryGetSymbolKey(symbol);
1595
+ AddSymbolMention(result, name, "call", targetSymbolKey, containerQualifiedName);
1596
+ }
1597
+
1598
+ private static void AddNewReference(CliParseResult result, ObjectCreationExpressionSyntax node, string content, SemanticModel? model)
1599
+ {
1600
+ var containerQualifiedName = GetContainerQualifiedName(model, node);
1601
+ var symbol = TryGetSymbol(model, node, declared: false);
1602
+ var name = symbol?.Name ?? node.Type.ToString();
1603
+ var targetSymbolKey = symbol == null ? null : TryGetSymbolKey(symbol);
1604
+ AddSymbolMention(result, name, "new", targetSymbolKey, containerQualifiedName);
1605
+ }
1606
+
1607
+ // Emit a reference row for type/namespace/field/property/event identifier
1608
+ // usages so find-references covers more than just call/new sites.
1609
+ // Skips: declarations, locals, parameters, type parameters, labels;
1610
+ // also skips identifiers that are already covered by AddCallReference
1611
+ // (the callee of an invocation) or AddNewReference (the type of a new
1612
+ // expression), so we don't emit duplicate spans.
1613
+ private static void AddIdentifierReference(CliParseResult result, SimpleNameSyntax node, string content, SemanticModel? model)
1614
+ {
1615
+ if (model == null) { return; }
1616
+ if (IsRedundantWithCallOrNew(node)) { return; }
1617
+ if (IsDeclarationName(node)) { return; }
1618
+
1619
+ ISymbol? symbol;
1620
+ try
1621
+ {
1622
+ var info = model.GetSymbolInfo(node);
1623
+ symbol = info.Symbol ?? info.CandidateSymbols.FirstOrDefault();
1624
+ }
1625
+ catch
1626
+ {
1627
+ return;
1628
+ }
1629
+ if (symbol == null) { return; }
1630
+
1631
+ // Filter to symbol kinds where find-references is meaningful.
1632
+ // Methods are still emitted so method-group references (delegates,
1633
+ // nameof, etc.) are captured; the call sites themselves are also
1634
+ // covered by AddCallReference but with a different (full-call) span.
1635
+ switch (symbol.Kind)
1636
+ {
1637
+ case SymbolKind.NamedType:
1638
+ case SymbolKind.Namespace:
1639
+ case SymbolKind.Field:
1640
+ case SymbolKind.Property:
1641
+ case SymbolKind.Event:
1642
+ case SymbolKind.Method:
1643
+ break;
1644
+ default:
1645
+ return;
1646
+ }
1647
+
1648
+ // For methods, skip the redundant case where this identifier is the
1649
+ // immediate callee of an InvocationExpression. AddCallReference has
1650
+ // already emitted a row for the whole invocation.
1651
+ if (symbol.Kind == SymbolKind.Method && IsImmediateInvocationTarget(node))
1652
+ {
1653
+ return;
1654
+ }
1655
+
1656
+ var refKind = symbol.Kind switch
1657
+ {
1658
+ SymbolKind.NamedType => "type",
1659
+ SymbolKind.Namespace => "type",
1660
+ _ => "reference",
1661
+ };
1662
+
1663
+ var key = TryGetSymbolKey(symbol);
1664
+ var containerQualifiedName = GetContainerQualifiedName(model, node);
1665
+ AddSymbolMention(result, node.Identifier.Text, refKind, key, containerQualifiedName);
1666
+ }
1667
+
1668
+ private static bool IsRedundantWithCallOrNew(SimpleNameSyntax node)
1669
+ {
1670
+ // Walk up the "name chain" so that for `A.B.C` we still consider the
1671
+ // outermost access when classifying redundancy.
1672
+ SyntaxNode current = node;
1673
+ while (current.Parent is MemberAccessExpressionSyntax memberAccess && memberAccess.Name == current)
1674
+ {
1675
+ current = memberAccess;
1676
+ }
1677
+ while (current.Parent is QualifiedNameSyntax qualified && qualified.Right == current)
1678
+ {
1679
+ current = qualified;
1680
+ }
1681
+ // Whole-callee of an invocation is already covered by AddCallReference.
1682
+ if (current.Parent is InvocationExpressionSyntax invocation && invocation.Expression == current)
1683
+ {
1684
+ return true;
1685
+ }
1686
+ // Whole-type of an object creation is already covered by AddNewReference.
1687
+ if (current.Parent is ObjectCreationExpressionSyntax creation && creation.Type == current)
1688
+ {
1689
+ return true;
1690
+ }
1691
+ return false;
1692
+ }
1693
+
1694
+ private static bool IsImmediateInvocationTarget(SimpleNameSyntax node)
1695
+ {
1696
+ SyntaxNode current = node;
1697
+ while (current.Parent is MemberAccessExpressionSyntax memberAccess && memberAccess.Name == current)
1698
+ {
1699
+ current = memberAccess;
1700
+ }
1701
+ return current.Parent is InvocationExpressionSyntax invocation && invocation.Expression == current;
1702
+ }
1703
+
1704
+ private static bool IsDeclarationName(SimpleNameSyntax node)
1705
+ {
1706
+ // SimpleName doesn't itself carry a Declarator, but it can appear as
1707
+ // the explicit-interface qualifier of one. The actual declared name
1708
+ // of a class/method is a regular SyntaxToken on the parent node, so
1709
+ // it never reaches this dispatch.
1710
+ var parent = node.Parent;
1711
+ if (parent is NameEqualsSyntax) { return true; } // `using X = ...;` (left side)
1712
+ if (parent is NameColonSyntax) { return true; } // `Method(name: value)`
1713
+ return false;
1714
+ }
1715
+
1716
+ private static string? ExtractName(ExpressionSyntax expr)
1717
+ {
1718
+ return expr switch
1719
+ {
1720
+ IdentifierNameSyntax id => id.Identifier.Text,
1721
+ MemberAccessExpressionSyntax mem => mem.Name.Identifier.Text,
1722
+ GenericNameSyntax gen => gen.Identifier.Text,
1723
+ _ => expr.ToString(),
1724
+ };
1725
+ }
1726
+
1727
+ private static string? ExtractReceiverName(ExpressionSyntax expr)
1728
+ {
1729
+ if (expr is MemberAccessExpressionSyntax mem)
1730
+ {
1731
+ if (mem.Expression is IdentifierNameSyntax id)
1732
+ {
1733
+ return id.Identifier.Text;
1734
+ }
1735
+ }
1736
+ return null;
1737
+ }
1738
+
1739
+ private static Dictionary<string, object?> BuildReferenceMetadata(SyntaxNode node, string content)
1740
+ {
1741
+ var metadata = new Dictionary<string, object?>();
1742
+ var text = content.Substring(node.Span.Start, node.Span.Length);
1743
+ metadata["expression"] = text;
1744
+
1745
+ var containerType = node.Ancestors().OfType<BaseTypeDeclarationSyntax>().FirstOrDefault();
1746
+ if (containerType != null)
1747
+ {
1748
+ metadata["containerName"] = containerType.Identifier.Text;
1749
+ metadata["containerKind"] = "Class";
1750
+ }
1751
+ return metadata;
1752
+ }
1753
+
1754
+ private static Dictionary<string, object?> BuildVisibilityMetadata(SyntaxTokenList modifiers, string? containerName = null, string? containerKind = null)
1755
+ {
1756
+ var metadata = new Dictionary<string, object?>();
1757
+ var visibility = "private";
1758
+ if (modifiers.Any(SyntaxKind.PublicKeyword)) { visibility = "public"; }
1759
+ else if (modifiers.Any(SyntaxKind.ProtectedKeyword) && modifiers.Any(SyntaxKind.InternalKeyword)) { visibility = "protected internal"; }
1760
+ else if (modifiers.Any(SyntaxKind.ProtectedKeyword)) { visibility = "protected"; }
1761
+ else if (modifiers.Any(SyntaxKind.InternalKeyword)) { visibility = "internal"; }
1762
+
1763
+ metadata["visibility"] = visibility;
1764
+ if (!string.IsNullOrWhiteSpace(containerName)) { metadata["containerName"] = containerName; }
1765
+ if (!string.IsNullOrWhiteSpace(containerKind)) { metadata["containerKind"] = "Class"; }
1766
+ return metadata;
1767
+ }
1768
+
1769
+ private static CliResolvedSpan ResolveSpan(string nodePath, CompilationUnitSyntax root, string content)
1770
+ {
1771
+ var targetRef = ParseNodePathRef(nodePath);
1772
+ if (targetRef == null)
1773
+ {
1774
+ return new CliResolvedSpan { Error = "invalid nodePath" };
1775
+ }
1776
+
1777
+ var target = FindNodeByRef(root, targetRef.Value.start, targetRef.Value.end, targetRef.Value.kind);
1778
+ if (target == null)
1779
+ {
1780
+ return new CliResolvedSpan { Error = "node not found" };
1781
+ }
1782
+
1783
+ int? bodyStart = null;
1784
+ int? bodyEnd = null;
1785
+
1786
+ switch (target)
1787
+ {
1788
+ case BaseTypeDeclarationSyntax typeDecl:
1789
+ bodyStart = typeDecl.OpenBraceToken.Span.End;
1790
+ bodyEnd = typeDecl.CloseBraceToken.SpanStart;
1791
+ break;
1792
+ case BaseMethodDeclarationSyntax method:
1793
+ if (method.Body != null)
1794
+ {
1795
+ bodyStart = method.Body.OpenBraceToken.Span.End;
1796
+ bodyEnd = method.Body.CloseBraceToken.SpanStart;
1797
+ }
1798
+ break;
1799
+ case AccessorDeclarationSyntax accessor:
1800
+ if (accessor.Body != null)
1801
+ {
1802
+ bodyStart = accessor.Body.OpenBraceToken.Span.End;
1803
+ bodyEnd = accessor.Body.CloseBraceToken.SpanStart;
1804
+ }
1805
+ break;
1806
+ }
1807
+
1808
+ return new CliResolvedSpan
1809
+ {
1810
+ Start = target.Span.Start,
1811
+ End = target.Span.End,
1812
+ BodyStart = bodyStart,
1813
+ BodyEnd = bodyEnd,
1814
+ Text = content.Substring(target.Span.Start, target.Span.Length),
1815
+ };
1816
+ }
1817
+
1818
+ private static SyntaxNode? FindNodeByRef(CompilationUnitSyntax root, int start, int end, string? kind)
1819
+ {
1820
+ SyntaxNode? firstSpanMatch = null;
1821
+ foreach (var node in root.DescendantNodes())
1822
+ {
1823
+ if (node.Span.Start != start || node.Span.End != end) { continue; }
1824
+ firstSpanMatch ??= node;
1825
+ if (!string.IsNullOrWhiteSpace(kind) && string.Equals(node.Kind().ToString(), kind, StringComparison.Ordinal))
1826
+ {
1827
+ return node;
1828
+ }
1829
+ }
1830
+ return firstSpanMatch;
1831
+ }
1832
+
1833
+ private static (int start, int end, string? kind)? ParseNodePathRef(string nodePath)
1834
+ {
1835
+ var clean = nodePath;
1836
+ if (clean.StartsWith("SyntaxError:") || clean.StartsWith("CompileError:"))
1837
+ {
1838
+ var idx = clean.IndexOf(':');
1839
+ if (idx >= 0) { clean = clean.Substring(idx + 1); }
1840
+ }
1841
+ string? kind = null;
1842
+ if (clean.Contains('#'))
1843
+ {
1844
+ clean = clean.Split('#')[1];
1845
+ }
1846
+ if (clean.Contains(':'))
1847
+ {
1848
+ kind = clean.Substring(clean.LastIndexOf(':') + 1);
1849
+ clean = clean.Substring(0, clean.LastIndexOf(':'));
1850
+ }
1851
+ var parts = clean.Split('-');
1852
+ if (parts.Length != 2) { return null; }
1853
+ if (int.TryParse(parts[0], out var start) && int.TryParse(parts[1], out var end) && end >= start)
1854
+ {
1855
+ return (start, end, string.IsNullOrWhiteSpace(kind) ? null : kind);
1856
+ }
1857
+ return null;
1858
+ }
1859
+ }
1860
+ }