Skip to content

Commit 80942ef

Browse files
committed
Merge pull request #66 from JoshVarty/master
Fixes #63 and miscellaneous bugs.
2 parents 688d4b6 + ab35462 commit 80942ef

File tree

7 files changed

+148
-111
lines changed

7 files changed

+148
-111
lines changed

src/SourceBrowser.Generator/DocumentWalkers/CSWalker.cs

+49-55
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)
128-
{
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)
126+
public Token ProcessSymbolUsage(SyntaxToken token, ISymbol symbol, bool isDeclaration)
153127
{
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,44 @@ 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+
string fullyQualifiedName;
172+
if (symbol.Kind == SymbolKind.Parameter)
173+
{
174+
var containingName = symbol.ContainingSymbol.ToString();
175+
fullyQualifiedName = containingName + CSharpDelimiters.PARAMETER + symbol.Name;
176+
}
177+
else if (symbol.Kind == SymbolKind.Local)
178+
{
179+
var containingName = symbol.ContainingSymbol.ToString();
180+
fullyQualifiedName = containingName + CSharpDelimiters.LOCAL_VARIABLE + symbol.Name;
181+
}
182+
else
183+
{
184+
fullyQualifiedName = symbol.ToString();
185+
}
186+
187+
return fullyQualifiedName;
188+
}
189+
188190
public Token ProcessIdentifier(SyntaxToken token)
189191
{
190192
//Check if this token is part of a declaration
191-
var parentSymbol = _model.GetDeclaredSymbol(token.Parent);
192-
if (parentSymbol != null)
193+
bool isDeclaration = false;
194+
if (_model.GetDeclaredSymbol(token.Parent) != null)
193195
{
194-
if(parentSymbol.Kind == SymbolKind.Parameter)
195-
{
196-
parentSymbol = parentSymbol.ContainingSymbol;
197-
}
198-
return ProcessDeclarationToken(token, parentSymbol);
196+
isDeclaration = true;
199197
}
200-
201-
//Find the symbol this token references
202-
var symbol = _model.GetSymbolInfo(token.Parent).Symbol;
198+
var startPosition = token.GetLocation().SourceSpan.Start;
199+
//Note: We're using the SymbolFinder as it correctly resolves
200+
var symbol = SymbolFinder.FindSymbolAtPosition(_model, startPosition, _document.Project.Solution.Workspace);
203201
if (symbol != null)
204202
{
205-
if(symbol.Kind == SymbolKind.Parameter)
206-
{
207-
symbol = symbol.ContainingSymbol;
208-
}
209-
return ProcessSymbolUsage(token, symbol) ;
203+
return ProcessSymbolUsage(token, symbol, isDeclaration);
210204
}
211205

212206
//Otherwise it references something we don't

src/SourceBrowser.Generator/DocumentWalkers/VBWalker.cs

+46-52
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)
143+
//Do not allow us to search locals
144+
if (symbol.Kind == SymbolKind.Local || symbol.Kind == SymbolKind.Parameter)
160145
{
161-
type = VisualBasicTokenTypes.TYPE;
146+
isSearchable = false;
162147
}
163-
else
164-
{
165-
type = VisualBasicTokenTypes.IDENTIFIER;
166-
}
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,41 @@ public Token ProcessSymbolUsage(SyntaxToken token, ISymbol symbol)
185166
return tokenModel;
186167
}
187168

169+
private string GetSymbolName(ISymbol symbol)
170+
{
171+
string fullyQualifiedName;
172+
if (symbol.Kind == SymbolKind.Parameter)
173+
{
174+
var containingName = symbol.ContainingSymbol.ToString();
175+
fullyQualifiedName = containingName + VBDelimiters.PARAMETER + symbol.Name;
176+
}
177+
else if (symbol.Kind == SymbolKind.Local)
178+
{
179+
var containingName = symbol.ContainingSymbol.ToString();
180+
fullyQualifiedName = containingName + VBDelimiters.LOCAL_VARIABLE + symbol.Name;
181+
}
182+
else
183+
{
184+
fullyQualifiedName = symbol.ToString();
185+
}
186+
187+
return fullyQualifiedName;
188+
}
189+
188190
public Token ProcessIdentifier(SyntaxToken token)
189191
{
190192
//Check if this token is part of a declaration
191-
var parentSymbol = _model.GetDeclaredSymbol(token.Parent);
192-
if (parentSymbol != null)
193+
bool isDeclaration = false;
194+
if (_model.GetDeclaredSymbol(token.Parent) != null)
193195
{
194-
if (parentSymbol.Kind == SymbolKind.Parameter)
195-
{
196-
parentSymbol = parentSymbol.ContainingSymbol;
197-
}
198-
return ProcessDeclarationToken(token, parentSymbol);
196+
isDeclaration = true;
199197
}
200-
201-
//Find the symbol this token references
202-
var symbol = _model.GetSymbolInfo(token.Parent).Symbol;
198+
var startPosition = token.GetLocation().SourceSpan.Start;
199+
//Note: We're using the SymbolFinder as it correctly resolves
200+
var symbol = SymbolFinder.FindSymbolAtPosition(_model, startPosition, _document.Project.Solution.Workspace);
203201
if (symbol != null)
204202
{
205-
if (symbol.Kind == SymbolKind.Parameter)
206-
{
207-
symbol = symbol.ContainingSymbol;
208-
}
209-
return ProcessSymbolUsage(token, symbol);
203+
return ProcessSymbolUsage(token, symbol, isDeclaration);
210204
}
211205

212206
//Otherwise it references something we don't
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace SourceBrowser.Generator.Model.CSharp
8+
{
9+
/// <summary>
10+
/// Fully qualified names do not contain enough information to differentiate
11+
/// between some symbols. Occasionally we must extend C#'s fully qualified names
12+
/// to refer to certain symbols.
13+
///
14+
/// Examples: Local variables, parameters, static constructors etc.
15+
/// </summary>
16+
public static class CSharpDelimiters
17+
{
18+
public const string LOCAL_VARIABLE = "::";
19+
public const string PARAMETER = "::";
20+
}
21+
}

0 commit comments

Comments
 (0)