Skip to content

Commit 4e83fe5

Browse files
committed
New approach for finding symbols.
This applies to #63 but also fixes various issues with local variables that hadn't been noticed yet. We desperately need tests.
1 parent f05e44b commit 4e83fe5

File tree

2 files changed

+81
-107
lines changed

2 files changed

+81
-107
lines changed

src/SourceBrowser.Generator/DocumentWalkers/CSWalker.cs

Lines changed: 42 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
using SourceBrowser.Generator.Extensions;
1111
using SourceBrowser.Generator.Model;
1212
using SourceBrowser.Generator.Model.CSharp;
13+
using Microsoft.CodeAnalysis.Shared.Extensions;
14+
using Microsoft.CodeAnalysis.FindSymbols;
1315

1416
namespace SourceBrowser.Generator.DocumentWalkers
1517
{
@@ -23,7 +25,9 @@ public class CSWalker : CSharpSyntaxWalker, IWalker
2325
public DocumentModel DocumentModel { get; private set; }
2426
public string FilePath { get; set; }
2527

26-
public CSWalker(IProjectItem parent, Document document, ReferencesourceLinkProvider refSourceLinkProvider): base(SyntaxWalkerDepth.Trivia)
28+
private Document _document;
29+
30+
public CSWalker(IProjectItem parent, Document document, ReferencesourceLinkProvider refSourceLinkProvider) : base(SyntaxWalkerDepth.Trivia)
2731
{
2832
_model = document.GetSemanticModelAsync().Result;
2933
_refsourceLinkProvider = refSourceLinkProvider;
@@ -33,13 +37,13 @@ public CSWalker(IProjectItem parent, Document document, ReferencesourceLinkProvi
3337
DocumentModel = new DocumentModel(parent, document.Name, numberOfLines);
3438
FilePath = document.GetRelativeFilePath();
3539
_refsourceLinkProvider = refSourceLinkProvider;
40+
_document = document;
3641
}
3742

3843
public override void VisitToken(SyntaxToken token)
3944
{
40-
string str = String.Empty;
4145
Token tokenModel = null;
42-
46+
4347
if (token.IsKeyword())
4448
{
4549
tokenModel = ProcessKeyword(token);
@@ -48,7 +52,7 @@ public override void VisitToken(SyntaxToken token)
4852
{
4953
tokenModel = ProcessIdentifier(token);
5054
}
51-
else if(token.CSharpKind() == SyntaxKind.StringLiteralToken)
55+
else if (token.CSharpKind() == SyntaxKind.StringLiteralToken)
5256
{
5357
tokenModel = ProcessStringLiteral(token);
5458
}
@@ -115,46 +119,17 @@ private Token ProcessStringLiteral(SyntaxToken token)
115119
string value = token.ToString();
116120
string type = CSharpTokenTypes.STRING;
117121
int lineNumber = token.GetLocation().GetLineSpan().StartLinePosition.Line + 1;
118-
119122
var tokenModel = new Token(this.DocumentModel, fullName, value, type, lineNumber);
120123
return tokenModel;
121124
}
122125

123-
/// <summary>
124-
/// Given a syntax token identifier that represents a declaration,
125-
/// generate and return the proper HTML for this symbol.
126-
/// </summary>
127-
public Token ProcessDeclarationToken(SyntaxToken token, ISymbol parentSymbol)
126+
public Token ProcessSymbolUsage(SyntaxToken token, ISymbol symbol, bool isDeclaration)
128127
{
129-
string fullName = parentSymbol.ToString();
130-
string value = token.ToString();
131-
string type = string.Empty;
132-
bool isDeclaration = true;
133-
int lineNumber = token.GetLocation().GetLineSpan().StartLinePosition.Line + 1;
134-
135-
if (parentSymbol is INamedTypeSymbol)
136-
{
137-
type = CSharpTokenTypes.TYPE;
138-
}
139-
else
140-
{
141-
type = CSharpTokenTypes.IDENTIFIER;
142-
}
143-
144-
var tokenModel = new Token(this.DocumentModel, fullName, value, type, lineNumber, isDeclaration);
145-
return tokenModel;
146-
}
147-
148-
/// <summary>
149-
/// Given a syntax token identifier that represents a symbol's usage
150-
/// generate and return the proper HTML for this symbol
151-
/// </summary>
152-
public Token ProcessSymbolUsage(SyntaxToken token, ISymbol symbol)
153-
{
154-
string fullName = symbol.ToString();
128+
string fullName = GetSymbolName(symbol);
155129
string value = token.ToString();
156130
string type = String.Empty;
157131
int lineNumber = token.GetLocation().GetLineSpan().StartLinePosition.Line + 1;
132+
bool isSearchable = isDeclaration;
158133

159134
if (symbol is INamedTypeSymbol)
160135
{
@@ -164,13 +139,20 @@ public Token ProcessSymbolUsage(SyntaxToken token, ISymbol symbol)
164139
{
165140
type = CSharpTokenTypes.IDENTIFIER;
166141
}
167-
var tokenModel = new Token(this.DocumentModel, fullName, value, type, lineNumber);
168-
142+
143+
//Do not allow us to search locals
144+
if (symbol.Kind == SymbolKind.Local || symbol.Kind == SymbolKind.Parameter)
145+
{
146+
isSearchable = false;
147+
}
148+
149+
var tokenModel = new Token(this.DocumentModel, fullName, value, type, lineNumber, isDeclaration, isSearchable);
150+
169151
//If we can find the declaration, we'll link it ourselves
170152
if (symbol.DeclaringSyntaxReferences.Any()
171153
&& !(symbol is INamespaceSymbol))
172154
{
173-
var link = new SymbolLink(referencedSymbolName: symbol.ToString());
155+
var link = new SymbolLink(referencedSymbolName: fullName);
174156
tokenModel = tokenModel.WithLink(link);
175157
}
176158
//Otherwise, we try to link to the .Net Reference source
@@ -181,32 +163,37 @@ public Token ProcessSymbolUsage(SyntaxToken token, ISymbol symbol)
181163
tokenModel = tokenModel.WithLink(link);
182164
}
183165

184-
185166
return tokenModel;
186167
}
187168

169+
private string GetSymbolName(ISymbol symbol)
170+
{
171+
if (symbol.Kind == SymbolKind.Parameter || symbol.Kind == SymbolKind.Local)
172+
{
173+
var containingName = symbol.ContainingSymbol.ToString();
174+
var name = containingName + "::" + symbol.Name;
175+
return name;
176+
}
177+
else
178+
{
179+
return symbol.ToString();
180+
}
181+
}
182+
188183
public Token ProcessIdentifier(SyntaxToken token)
189184
{
190185
//Check if this token is part of a declaration
191-
var parentSymbol = _model.GetDeclaredSymbol(token.Parent);
192-
if (parentSymbol != null)
186+
bool isDeclaration = false;
187+
if (_model.GetDeclaredSymbol(token.Parent) != null)
193188
{
194-
if(parentSymbol.Kind == SymbolKind.Parameter)
195-
{
196-
parentSymbol = parentSymbol.ContainingSymbol;
197-
}
198-
return ProcessDeclarationToken(token, parentSymbol);
189+
isDeclaration = true;
199190
}
200-
201-
//Find the symbol this token references
202-
var symbol = _model.GetSymbolInfo(token.Parent).Symbol;
191+
var startPosition = token.GetLocation().SourceSpan.Start;
192+
//Note: We're using the SymbolFinder as it correctly resolves
193+
var symbol = SymbolFinder.FindSymbolAtPosition(_model, startPosition, _document.Project.Solution.Workspace);
203194
if (symbol != null)
204195
{
205-
if(symbol.Kind == SymbolKind.Parameter)
206-
{
207-
symbol = symbol.ContainingSymbol;
208-
}
209-
return ProcessSymbolUsage(token, symbol) ;
196+
return ProcessSymbolUsage(token, symbol, isDeclaration);
210197
}
211198

212199
//Otherwise it references something we don't

src/SourceBrowser.Generator/DocumentWalkers/VBWalker.cs

Lines changed: 39 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
using SourceBrowser.Generator.Extensions;
1111
using SourceBrowser.Generator.Model;
1212
using SourceBrowser.Generator.Model.VisualBasic;
13+
using Microsoft.CodeAnalysis.Shared.Extensions;
14+
using Microsoft.CodeAnalysis.FindSymbols;
1315

1416
namespace SourceBrowser.Generator.DocumentWalkers
1517
{
@@ -23,7 +25,9 @@ public class VBWalker : VisualBasicSyntaxWalker, IWalker
2325
public DocumentModel DocumentModel { get; private set; }
2426
public string FilePath { get; set; }
2527

26-
public VBWalker(IProjectItem parent, Document document, ReferencesourceLinkProvider refSourceLinkProvider): base(SyntaxWalkerDepth.Trivia)
28+
private Document _document;
29+
30+
public VBWalker(IProjectItem parent, Document document, ReferencesourceLinkProvider refSourceLinkProvider) : base(SyntaxWalkerDepth.Trivia)
2731
{
2832
_model = document.GetSemanticModelAsync().Result;
2933
_refsourceLinkProvider = refSourceLinkProvider;
@@ -33,12 +37,13 @@ public VBWalker(IProjectItem parent, Document document, ReferencesourceLinkProvi
3337
DocumentModel = new DocumentModel(parent, document.Name, numberOfLines);
3438
FilePath = document.GetRelativeFilePath();
3539
_refsourceLinkProvider = refSourceLinkProvider;
40+
_document = document;
3641
}
3742

3843
public override void VisitToken(SyntaxToken token)
3944
{
4045
Token tokenModel = null;
41-
46+
4247
if (token.IsKeyword())
4348
{
4449
tokenModel = ProcessKeyword(token);
@@ -47,7 +52,7 @@ public override void VisitToken(SyntaxToken token)
4752
{
4853
tokenModel = ProcessIdentifier(token);
4954
}
50-
else if(token.VisualBasicKind() == SyntaxKind.StringLiteralToken)
55+
else if (token.VisualBasicKind() == SyntaxKind.StringLiteralToken)
5156
{
5257
tokenModel = ProcessStringLiteral(token);
5358
}
@@ -92,7 +97,6 @@ private Token ProcessOtherToken(SyntaxToken token)
9297
int lineNumber = token.GetLocation().GetLineSpan().StartLinePosition.Line + 1;
9398

9499
var tokenModel = new Token(this.DocumentModel, fullName, value, type, lineNumber);
95-
96100
return tokenModel;
97101
}
98102

@@ -105,7 +109,6 @@ public Token ProcessKeyword(SyntaxToken token)
105109
string value = token.ToString();
106110
string type = VisualBasicTokenTypes.KEYWORD;
107111
int lineNumber = token.GetLocation().GetLineSpan().StartLinePosition.Line + 1;
108-
109112
var tokenModel = new Token(this.DocumentModel, fullName, value, type, lineNumber);
110113
return tokenModel;
111114
}
@@ -116,24 +119,19 @@ private Token ProcessStringLiteral(SyntaxToken token)
116119
string value = token.ToString();
117120
string type = VisualBasicTokenTypes.STRING;
118121
int lineNumber = token.GetLocation().GetLineSpan().StartLinePosition.Line + 1;
119-
120122
var tokenModel = new Token(this.DocumentModel, fullName, value, type, lineNumber);
121123
return tokenModel;
122124
}
123125

124-
/// <summary>
125-
/// Given a syntax token identifier that represents a declaration,
126-
/// generate and return the proper HTML for this symbol.
127-
/// </summary>
128-
public Token ProcessDeclarationToken(SyntaxToken token, ISymbol parentSymbol)
126+
public Token ProcessSymbolUsage(SyntaxToken token, ISymbol symbol, bool isDeclaration)
129127
{
130-
string fullName = parentSymbol.ToString();
128+
string fullName = GetSymbolName(symbol);
131129
string value = token.ToString();
132-
string type = string.Empty;
130+
string type = String.Empty;
133131
int lineNumber = token.GetLocation().GetLineSpan().StartLinePosition.Line + 1;
134-
bool isDeclaration = true;
132+
bool isSearchable = isDeclaration;
135133

136-
if (parentSymbol is INamedTypeSymbol)
134+
if (symbol is INamedTypeSymbol)
137135
{
138136
type = VisualBasicTokenTypes.TYPE;
139137
}
@@ -142,36 +140,19 @@ public Token ProcessDeclarationToken(SyntaxToken token, ISymbol parentSymbol)
142140
type = VisualBasicTokenTypes.IDENTIFIER;
143141
}
144142

145-
var tokenModel = new Token(this.DocumentModel, fullName, value, type, lineNumber, isDeclaration);
146-
return tokenModel;
147-
}
148-
149-
/// <summary>
150-
/// Given a syntax token identifier that represents a symbol's usage
151-
/// generate and return the proper HTML for this symbol
152-
/// </summary>
153-
public Token ProcessSymbolUsage(SyntaxToken token, ISymbol symbol)
154-
{
155-
string fullName = symbol.ToString();
156-
string value = token.ToString();
157-
string type = string.Empty;
158-
int lineNumber = token.GetLocation().GetLineSpan().StartLinePosition.Line + 1;
159-
if (symbol is INamedTypeSymbol)
160-
{
161-
type = VisualBasicTokenTypes.TYPE;
162-
}
163-
else
143+
//Do not allow us to search locals
144+
if (symbol.Kind == SymbolKind.Local || symbol.Kind == SymbolKind.Parameter)
164145
{
165-
type = VisualBasicTokenTypes.IDENTIFIER;
146+
isSearchable = false;
166147
}
167-
148+
168149
var tokenModel = new Token(this.DocumentModel, fullName, value, type, lineNumber);
169150

170151
//If we can find the declaration, we'll link it ourselves
171152
if (symbol.DeclaringSyntaxReferences.Any()
172153
&& !(symbol is INamespaceSymbol))
173154
{
174-
var link = new SymbolLink(referencedSymbolName: symbol.ToString());
155+
var link = new SymbolLink(referencedSymbolName: fullName);
175156
tokenModel = tokenModel.WithLink(link);
176157
}
177158
//Otherwise, we try to link to the .Net Reference source
@@ -185,28 +166,34 @@ public Token ProcessSymbolUsage(SyntaxToken token, ISymbol symbol)
185166
return tokenModel;
186167
}
187168

169+
private string GetSymbolName(ISymbol symbol)
170+
{
171+
if (symbol.Kind == SymbolKind.Parameter || symbol.Kind == SymbolKind.Local)
172+
{
173+
var containingName = symbol.ContainingSymbol.ToString();
174+
var name = containingName + "::" + symbol.Name;
175+
return name;
176+
}
177+
else
178+
{
179+
return symbol.ToString();
180+
}
181+
}
182+
188183
public Token ProcessIdentifier(SyntaxToken token)
189184
{
190185
//Check if this token is part of a declaration
191-
var parentSymbol = _model.GetDeclaredSymbol(token.Parent);
192-
if (parentSymbol != null)
186+
bool isDeclaration = false;
187+
if (_model.GetDeclaredSymbol(token.Parent) != null)
193188
{
194-
if (parentSymbol.Kind == SymbolKind.Parameter)
195-
{
196-
parentSymbol = parentSymbol.ContainingSymbol;
197-
}
198-
return ProcessDeclarationToken(token, parentSymbol);
189+
isDeclaration = true;
199190
}
200-
201-
//Find the symbol this token references
202-
var symbol = _model.GetSymbolInfo(token.Parent).Symbol;
191+
var startPosition = token.GetLocation().SourceSpan.Start;
192+
//Note: We're using the SymbolFinder as it correctly resolves
193+
var symbol = SymbolFinder.FindSymbolAtPosition(_model, startPosition, _document.Project.Solution.Workspace);
203194
if (symbol != null)
204195
{
205-
if (symbol.Kind == SymbolKind.Parameter)
206-
{
207-
symbol = symbol.ContainingSymbol;
208-
}
209-
return ProcessSymbolUsage(token, symbol);
196+
return ProcessSymbolUsage(token, symbol, isDeclaration);
210197
}
211198

212199
//Otherwise it references something we don't

0 commit comments

Comments
 (0)