Using Roslyn, the only mechanism for determining members of Visual Basic document appears to be:
var members = SyntaxTree.GetRoot().DescendantNodes().Where(node =>
node is ClassStatementSyntax ||
node is FunctionAggregationSyntax ||
node is IncompleteMemberSyntax ||
node is MethodBaseSyntax ||
node is ModuleStatementSyntax ||
node is NamespaceStatementSyntax ||
node is PropertyStatementSyntax ||
node is SubNewStatementSyntax
);
How do get the member name, StarLineNumber and EndLineNumber of each member?
Exists not only the one way to get it:
1) As you try: I willn't show this way for all of kind member (they count are huge and the logic is the similar), but only a one of them, for example ClassStatementSyntax
:
- to achive it name just get
ClassStatementSyntax.Identifier.ValueText
- to get start line you can use
Location
as one of ways:
var location = Location.Create(SyntaxTree, ClassStatementSyntax.Identifier.Span);
var startLine = location.GetLineSpan().StartLinePosition.Line;
- logic for retrieving the end line looks like a logic to receive the start line but it dependents on the corresponding closing statement (some kind of end statement or self)
2) More useful way – use SemanticModel
to get a data that you want:
In this way you will need to receive semantic info only for ClassStatementSyntax
, ModuleStatementSyntxt
and NamespaceStatementSyntax
, and all of their members will be received just calling GetMembers()
:
...
SemanticModel semanticModel = // usually it is received from the corresponding compilation
var typeSyntax = // ClassStatementSyntax, ModuleStatementSyntxt or NamespaceStatementSyntax
string name = null;
int startLine;
int endLine;
var info = semanticModel.GetSymbolInfo(typeSyntax);
if (info.Symbol is INamespaceOrTypeSymbol typeSymbol)
{
name = typeSymbol.Name; // retrieve Name
startLine = semanticModel.SyntaxTree.GetLineSpan(typeSymbol.DeclaringSyntaxReferences[0].Span).StartLinePosition.Line; //retrieve start line
endLine = semanticModel.SyntaxTree.GetLineSpan(typeSymbol.DeclaringSyntaxReferences[0].Span).EndLinePosition.Line; //retrieve end line
foreach (var item in typeSymbol.GetMembers())
{
// do the same logic for retrieving name and lines for all others members without calling GetMembers()
}
}
else if (semanticModel.GetDeclaredSymbol(typeSyntax) is INamespaceOrTypeSymbol typeSymbol2)
{
name = typeSymbol2.Name; // retrieve Name
startLine = semanticModel.SyntaxTree.GetLineSpan(typeSymbol2.DeclaringSyntaxReferences[0].Span).StartLinePosition.Line; //retrieve start line
endLine = semanticModel.SyntaxTree.GetLineSpan(typeSymbol2.DeclaringSyntaxReferences[0].Span).EndLinePosition.Line; //retrieve end line
foreach (var item in typeSymbol2.GetMembers())
{
// do the same logic for retrieving name and lines for all others members without calling GetMembers()
}
}
But attention, when you have a partial declaration your DeclaringSyntaxReferences
will have a couple items, so you need to filter SyntaxReference
by your current SyntaxTree