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.
- package/dist/cli.js +124 -124
- package/dist/watcher-process.js +421 -421
- package/dist/workers/chunk-Z7LW7C4Z.mjs +518 -0
- package/dist/workers/indexer-worker.mjs +1 -1
- package/dist/workers/snapshot-worker.mjs +8 -4
- package/package.json +5 -4
- package/resources/tools/{pando-tools.json → generated_pando-tools.json} +98 -4
- package/resources/tools/generated_tools.md +93 -0
- package/tools/clojure-editor/lib/pando-clojure-editor-standalone.jar +0 -0
- package/tools/clojure-indexer/lib/pando-clojure-indexer-standalone.jar +0 -0
- package/tools/csharp-indexer/CSharpIndexer.csproj +1 -1
- package/tools/csharp-indexer/bin/pando-csharp-indexer +81 -23
- package/tools/csharp-indexer/bin/pando-csharp-indexer.cmd +82 -14
- package/tools/csharp-indexer/src/Program.cs +1860 -0
- package/dist/workers/chunk-BD3AO7O3.mjs +0 -522
- package/tools/csharp-indexer/bin/Release/net10.0/CSharpIndexer +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/CSharpIndexer.deps.json +0 -383
- package/tools/csharp-indexer/bin/Release/net10.0/CSharpIndexer.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/CSharpIndexer.pdb +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/CSharpIndexer.runtimeconfig.json +0 -13
- package/tools/csharp-indexer/bin/Release/net10.0/Humanizer.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/Microsoft.CodeAnalysis.CSharp.Workspaces.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/Microsoft.CodeAnalysis.CSharp.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/Microsoft.CodeAnalysis.Workspaces.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/Microsoft.CodeAnalysis.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/System.Composition.AttributedModel.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/System.Composition.Convention.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/System.Composition.Hosting.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/System.Composition.Runtime.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/System.Composition.TypedParts.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/cs/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/cs/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/cs/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/cs/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/de/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/de/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/de/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/de/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/es/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/es/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/es/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/es/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/fr/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/fr/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/fr/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/fr/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/it/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/it/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/it/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/it/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/ja/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/ja/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/ja/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/ja/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/ko/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/ko/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/ko/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/ko/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/pl/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/pl/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/pl/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/pl/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/pt-BR/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/pt-BR/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/pt-BR/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/ru/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/ru/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/ru/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/ru/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/tr/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/tr/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/tr/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/tr/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/zh-Hans/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/zh-Hans/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/zh-Hant/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net10.0/zh-Hant/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/CSharpIndexer +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/CSharpIndexer.deps.json +0 -400
- package/tools/csharp-indexer/bin/Release/net8.0/CSharpIndexer.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/CSharpIndexer.pdb +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/CSharpIndexer.runtimeconfig.json +0 -13
- package/tools/csharp-indexer/bin/Release/net8.0/Humanizer.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/Microsoft.CodeAnalysis.CSharp.Workspaces.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/Microsoft.CodeAnalysis.CSharp.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/Microsoft.CodeAnalysis.Workspaces.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/Microsoft.CodeAnalysis.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/System.Composition.AttributedModel.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/System.Composition.Convention.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/System.Composition.Hosting.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/System.Composition.Runtime.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/System.Composition.TypedParts.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/System.IO.Pipelines.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/cs/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/cs/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/cs/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/cs/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/de/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/de/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/de/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/de/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/es/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/es/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/es/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/es/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/fr/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/fr/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/fr/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/fr/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/it/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/it/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/it/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/it/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/ja/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/ja/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/ja/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/ja/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/ko/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/ko/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/ko/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/ko/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/pl/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/pl/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/pl/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/pl/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/pt-BR/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/pt-BR/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/pt-BR/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/pt-BR/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/ru/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/ru/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/ru/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/ru/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/tr/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/tr/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/tr/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/tr/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/zh-Hans/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/zh-Hans/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/zh-Hans/Microsoft.CodeAnalysis.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.Workspaces.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/zh-Hant/Microsoft.CodeAnalysis.CSharp.resources.dll +0 -0
- package/tools/csharp-indexer/bin/Release/net8.0/zh-Hant/Microsoft.CodeAnalysis.Workspaces.resources.dll +0 -0
- 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
|
+
}
|