diff --git a/src/SourceBrowser.Generator/DocumentWalkers/CSWalker.cs b/src/SourceBrowser.Generator/DocumentWalkers/CSWalker.cs
index 351fb42..e479c03 100644
--- a/src/SourceBrowser.Generator/DocumentWalkers/CSWalker.cs
+++ b/src/SourceBrowser.Generator/DocumentWalkers/CSWalker.cs
@@ -10,6 +10,8 @@
using SourceBrowser.Generator.Extensions;
using SourceBrowser.Generator.Model;
using SourceBrowser.Generator.Model.CSharp;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.FindSymbols;
namespace SourceBrowser.Generator.DocumentWalkers
{
@@ -23,7 +25,9 @@ public class CSWalker : CSharpSyntaxWalker, IWalker
public DocumentModel DocumentModel { get; private set; }
public string FilePath { get; set; }
- public CSWalker(IProjectItem parent, Document document, ReferencesourceLinkProvider refSourceLinkProvider): base(SyntaxWalkerDepth.Trivia)
+ private Document _document;
+
+ public CSWalker(IProjectItem parent, Document document, ReferencesourceLinkProvider refSourceLinkProvider) : base(SyntaxWalkerDepth.Trivia)
{
_model = document.GetSemanticModelAsync().Result;
_refsourceLinkProvider = refSourceLinkProvider;
@@ -33,13 +37,13 @@ public CSWalker(IProjectItem parent, Document document, ReferencesourceLinkProvi
DocumentModel = new DocumentModel(parent, document.Name, numberOfLines);
FilePath = document.GetRelativeFilePath();
_refsourceLinkProvider = refSourceLinkProvider;
+ _document = document;
}
public override void VisitToken(SyntaxToken token)
{
- string str = String.Empty;
Token tokenModel = null;
-
+
if (token.IsKeyword())
{
tokenModel = ProcessKeyword(token);
@@ -48,7 +52,7 @@ public override void VisitToken(SyntaxToken token)
{
tokenModel = ProcessIdentifier(token);
}
- else if(token.CSharpKind() == SyntaxKind.StringLiteralToken)
+ else if (token.CSharpKind() == SyntaxKind.StringLiteralToken)
{
tokenModel = ProcessStringLiteral(token);
}
@@ -115,46 +119,17 @@ private Token ProcessStringLiteral(SyntaxToken token)
string value = token.ToString();
string type = CSharpTokenTypes.STRING;
int lineNumber = token.GetLocation().GetLineSpan().StartLinePosition.Line + 1;
-
var tokenModel = new Token(this.DocumentModel, fullName, value, type, lineNumber);
return tokenModel;
}
- ///
- /// Given a syntax token identifier that represents a declaration,
- /// generate and return the proper HTML for this symbol.
- ///
- public Token ProcessDeclarationToken(SyntaxToken token, ISymbol parentSymbol)
- {
- string fullName = parentSymbol.ToString();
- string value = token.ToString();
- string type = string.Empty;
- bool isDeclaration = true;
- int lineNumber = token.GetLocation().GetLineSpan().StartLinePosition.Line + 1;
-
- if (parentSymbol is INamedTypeSymbol)
- {
- type = CSharpTokenTypes.TYPE;
- }
- else
- {
- type = CSharpTokenTypes.IDENTIFIER;
- }
-
- var tokenModel = new Token(this.DocumentModel, fullName, value, type, lineNumber, isDeclaration);
- return tokenModel;
- }
-
- ///
- /// Given a syntax token identifier that represents a symbol's usage
- /// generate and return the proper HTML for this symbol
- ///
- public Token ProcessSymbolUsage(SyntaxToken token, ISymbol symbol)
+ public Token ProcessSymbolUsage(SyntaxToken token, ISymbol symbol, bool isDeclaration)
{
- string fullName = symbol.ToString();
+ string fullName = GetSymbolName(symbol);
string value = token.ToString();
string type = String.Empty;
int lineNumber = token.GetLocation().GetLineSpan().StartLinePosition.Line + 1;
+ bool isSearchable = isDeclaration;
if (symbol is INamedTypeSymbol)
{
@@ -164,13 +139,20 @@ public Token ProcessSymbolUsage(SyntaxToken token, ISymbol symbol)
{
type = CSharpTokenTypes.IDENTIFIER;
}
- var tokenModel = new Token(this.DocumentModel, fullName, value, type, lineNumber);
-
+
+ //Do not allow us to search locals
+ if (symbol.Kind == SymbolKind.Local || symbol.Kind == SymbolKind.Parameter)
+ {
+ isSearchable = false;
+ }
+
+ var tokenModel = new Token(this.DocumentModel, fullName, value, type, lineNumber, isDeclaration, isSearchable);
+
//If we can find the declaration, we'll link it ourselves
if (symbol.DeclaringSyntaxReferences.Any()
&& !(symbol is INamespaceSymbol))
{
- var link = new SymbolLink(referencedSymbolName: symbol.ToString());
+ var link = new SymbolLink(referencedSymbolName: fullName);
tokenModel = tokenModel.WithLink(link);
}
//Otherwise, we try to link to the .Net Reference source
@@ -181,32 +163,44 @@ public Token ProcessSymbolUsage(SyntaxToken token, ISymbol symbol)
tokenModel = tokenModel.WithLink(link);
}
-
return tokenModel;
}
+ private string GetSymbolName(ISymbol symbol)
+ {
+ string fullyQualifiedName;
+ if (symbol.Kind == SymbolKind.Parameter)
+ {
+ var containingName = symbol.ContainingSymbol.ToString();
+ fullyQualifiedName = containingName + CSharpDelimiters.PARAMETER + symbol.Name;
+ }
+ else if (symbol.Kind == SymbolKind.Local)
+ {
+ var containingName = symbol.ContainingSymbol.ToString();
+ fullyQualifiedName = containingName + CSharpDelimiters.LOCAL_VARIABLE + symbol.Name;
+ }
+ else
+ {
+ fullyQualifiedName = symbol.ToString();
+ }
+
+ return fullyQualifiedName;
+ }
+
public Token ProcessIdentifier(SyntaxToken token)
{
//Check if this token is part of a declaration
- var parentSymbol = _model.GetDeclaredSymbol(token.Parent);
- if (parentSymbol != null)
+ bool isDeclaration = false;
+ if (_model.GetDeclaredSymbol(token.Parent) != null)
{
- if(parentSymbol.Kind == SymbolKind.Parameter)
- {
- parentSymbol = parentSymbol.ContainingSymbol;
- }
- return ProcessDeclarationToken(token, parentSymbol);
+ isDeclaration = true;
}
-
- //Find the symbol this token references
- var symbol = _model.GetSymbolInfo(token.Parent).Symbol;
+ var startPosition = token.GetLocation().SourceSpan.Start;
+ //Note: We're using the SymbolFinder as it correctly resolves
+ var symbol = SymbolFinder.FindSymbolAtPosition(_model, startPosition, _document.Project.Solution.Workspace);
if (symbol != null)
{
- if(symbol.Kind == SymbolKind.Parameter)
- {
- symbol = symbol.ContainingSymbol;
- }
- return ProcessSymbolUsage(token, symbol) ;
+ return ProcessSymbolUsage(token, symbol, isDeclaration);
}
//Otherwise it references something we don't
diff --git a/src/SourceBrowser.Generator/DocumentWalkers/VBWalker.cs b/src/SourceBrowser.Generator/DocumentWalkers/VBWalker.cs
index 0bdc4a1..8457ee0 100644
--- a/src/SourceBrowser.Generator/DocumentWalkers/VBWalker.cs
+++ b/src/SourceBrowser.Generator/DocumentWalkers/VBWalker.cs
@@ -10,6 +10,8 @@
using SourceBrowser.Generator.Extensions;
using SourceBrowser.Generator.Model;
using SourceBrowser.Generator.Model.VisualBasic;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.FindSymbols;
namespace SourceBrowser.Generator.DocumentWalkers
{
@@ -23,7 +25,9 @@ public class VBWalker : VisualBasicSyntaxWalker, IWalker
public DocumentModel DocumentModel { get; private set; }
public string FilePath { get; set; }
- public VBWalker(IProjectItem parent, Document document, ReferencesourceLinkProvider refSourceLinkProvider): base(SyntaxWalkerDepth.Trivia)
+ private Document _document;
+
+ public VBWalker(IProjectItem parent, Document document, ReferencesourceLinkProvider refSourceLinkProvider) : base(SyntaxWalkerDepth.Trivia)
{
_model = document.GetSemanticModelAsync().Result;
_refsourceLinkProvider = refSourceLinkProvider;
@@ -33,12 +37,13 @@ public VBWalker(IProjectItem parent, Document document, ReferencesourceLinkProvi
DocumentModel = new DocumentModel(parent, document.Name, numberOfLines);
FilePath = document.GetRelativeFilePath();
_refsourceLinkProvider = refSourceLinkProvider;
+ _document = document;
}
public override void VisitToken(SyntaxToken token)
{
Token tokenModel = null;
-
+
if (token.IsKeyword())
{
tokenModel = ProcessKeyword(token);
@@ -47,7 +52,7 @@ public override void VisitToken(SyntaxToken token)
{
tokenModel = ProcessIdentifier(token);
}
- else if(token.VisualBasicKind() == SyntaxKind.StringLiteralToken)
+ else if (token.VisualBasicKind() == SyntaxKind.StringLiteralToken)
{
tokenModel = ProcessStringLiteral(token);
}
@@ -92,7 +97,6 @@ private Token ProcessOtherToken(SyntaxToken token)
int lineNumber = token.GetLocation().GetLineSpan().StartLinePosition.Line + 1;
var tokenModel = new Token(this.DocumentModel, fullName, value, type, lineNumber);
-
return tokenModel;
}
@@ -105,7 +109,6 @@ public Token ProcessKeyword(SyntaxToken token)
string value = token.ToString();
string type = VisualBasicTokenTypes.KEYWORD;
int lineNumber = token.GetLocation().GetLineSpan().StartLinePosition.Line + 1;
-
var tokenModel = new Token(this.DocumentModel, fullName, value, type, lineNumber);
return tokenModel;
}
@@ -116,24 +119,19 @@ private Token ProcessStringLiteral(SyntaxToken token)
string value = token.ToString();
string type = VisualBasicTokenTypes.STRING;
int lineNumber = token.GetLocation().GetLineSpan().StartLinePosition.Line + 1;
-
var tokenModel = new Token(this.DocumentModel, fullName, value, type, lineNumber);
return tokenModel;
}
- ///
- /// Given a syntax token identifier that represents a declaration,
- /// generate and return the proper HTML for this symbol.
- ///
- public Token ProcessDeclarationToken(SyntaxToken token, ISymbol parentSymbol)
+ public Token ProcessSymbolUsage(SyntaxToken token, ISymbol symbol, bool isDeclaration)
{
- string fullName = parentSymbol.ToString();
+ string fullName = GetSymbolName(symbol);
string value = token.ToString();
- string type = string.Empty;
+ string type = String.Empty;
int lineNumber = token.GetLocation().GetLineSpan().StartLinePosition.Line + 1;
- bool isDeclaration = true;
+ bool isSearchable = isDeclaration;
- if (parentSymbol is INamedTypeSymbol)
+ if (symbol is INamedTypeSymbol)
{
type = VisualBasicTokenTypes.TYPE;
}
@@ -142,36 +140,19 @@ public Token ProcessDeclarationToken(SyntaxToken token, ISymbol parentSymbol)
type = VisualBasicTokenTypes.IDENTIFIER;
}
- var tokenModel = new Token(this.DocumentModel, fullName, value, type, lineNumber, isDeclaration);
- return tokenModel;
- }
-
- ///
- /// Given a syntax token identifier that represents a symbol's usage
- /// generate and return the proper HTML for this symbol
- ///
- public Token ProcessSymbolUsage(SyntaxToken token, ISymbol symbol)
- {
- string fullName = symbol.ToString();
- string value = token.ToString();
- string type = string.Empty;
- int lineNumber = token.GetLocation().GetLineSpan().StartLinePosition.Line + 1;
- if (symbol is INamedTypeSymbol)
+ //Do not allow us to search locals
+ if (symbol.Kind == SymbolKind.Local || symbol.Kind == SymbolKind.Parameter)
{
- type = VisualBasicTokenTypes.TYPE;
+ isSearchable = false;
}
- else
- {
- type = VisualBasicTokenTypes.IDENTIFIER;
- }
-
+
var tokenModel = new Token(this.DocumentModel, fullName, value, type, lineNumber);
//If we can find the declaration, we'll link it ourselves
if (symbol.DeclaringSyntaxReferences.Any()
&& !(symbol is INamespaceSymbol))
{
- var link = new SymbolLink(referencedSymbolName: symbol.ToString());
+ var link = new SymbolLink(referencedSymbolName: fullName);
tokenModel = tokenModel.WithLink(link);
}
//Otherwise, we try to link to the .Net Reference source
@@ -185,28 +166,41 @@ public Token ProcessSymbolUsage(SyntaxToken token, ISymbol symbol)
return tokenModel;
}
+ private string GetSymbolName(ISymbol symbol)
+ {
+ string fullyQualifiedName;
+ if (symbol.Kind == SymbolKind.Parameter)
+ {
+ var containingName = symbol.ContainingSymbol.ToString();
+ fullyQualifiedName = containingName + VBDelimiters.PARAMETER + symbol.Name;
+ }
+ else if (symbol.Kind == SymbolKind.Local)
+ {
+ var containingName = symbol.ContainingSymbol.ToString();
+ fullyQualifiedName = containingName + VBDelimiters.LOCAL_VARIABLE + symbol.Name;
+ }
+ else
+ {
+ fullyQualifiedName = symbol.ToString();
+ }
+
+ return fullyQualifiedName;
+ }
+
public Token ProcessIdentifier(SyntaxToken token)
{
//Check if this token is part of a declaration
- var parentSymbol = _model.GetDeclaredSymbol(token.Parent);
- if (parentSymbol != null)
+ bool isDeclaration = false;
+ if (_model.GetDeclaredSymbol(token.Parent) != null)
{
- if (parentSymbol.Kind == SymbolKind.Parameter)
- {
- parentSymbol = parentSymbol.ContainingSymbol;
- }
- return ProcessDeclarationToken(token, parentSymbol);
+ isDeclaration = true;
}
-
- //Find the symbol this token references
- var symbol = _model.GetSymbolInfo(token.Parent).Symbol;
+ var startPosition = token.GetLocation().SourceSpan.Start;
+ //Note: We're using the SymbolFinder as it correctly resolves
+ var symbol = SymbolFinder.FindSymbolAtPosition(_model, startPosition, _document.Project.Solution.Workspace);
if (symbol != null)
{
- if (symbol.Kind == SymbolKind.Parameter)
- {
- symbol = symbol.ContainingSymbol;
- }
- return ProcessSymbolUsage(token, symbol);
+ return ProcessSymbolUsage(token, symbol, isDeclaration);
}
//Otherwise it references something we don't
diff --git a/src/SourceBrowser.Generator/Model/CSharp/CSharpDelimiters.cs b/src/SourceBrowser.Generator/Model/CSharp/CSharpDelimiters.cs
new file mode 100644
index 0000000..b29e900
--- /dev/null
+++ b/src/SourceBrowser.Generator/Model/CSharp/CSharpDelimiters.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SourceBrowser.Generator.Model.CSharp
+{
+ ///
+ /// Fully qualified names do not contain enough information to differentiate
+ /// between some symbols. Occasionally we must extend C#'s fully qualified names
+ /// to refer to certain symbols.
+ ///
+ /// Examples: Local variables, parameters, static constructors etc.
+ ///
+ public static class CSharpDelimiters
+ {
+ public const string LOCAL_VARIABLE = "::";
+ public const string PARAMETER = "::";
+ }
+}
diff --git a/src/SourceBrowser.Generator/Model/Token.cs b/src/SourceBrowser.Generator/Model/Token.cs
index cac6f73..4039f1e 100644
--- a/src/SourceBrowser.Generator/Model/Token.cs
+++ b/src/SourceBrowser.Generator/Model/Token.cs
@@ -24,9 +24,11 @@ public class Token
public bool IsDeclaration { get; }
+ public bool IsSearchable { get; }
+
public DocumentModel Document { get; }
- public Token(DocumentModel document, string fullName, string value, string type, int lineNumber, bool isDeclaration = false)
+ public Token(DocumentModel document, string fullName, string value, string type, int lineNumber, bool isDeclaration = false, bool isSearchable = false)
{
Document = document;
FullName = fullName;
@@ -34,9 +36,11 @@ public Token(DocumentModel document, string fullName, string value, string type,
Type = type;
LineNumber = lineNumber;
IsDeclaration = isDeclaration;
+ IsSearchable = isSearchable;
}
- private Token(Token oldToken, ILink link) : this(oldToken.Document, oldToken.FullName, oldToken.Value, oldToken.Type, oldToken.LineNumber, oldToken.IsDeclaration)
+ private Token(Token oldToken, ILink link) :
+ this(oldToken.Document, oldToken.FullName, oldToken.Value, oldToken.Type, oldToken.LineNumber, oldToken.IsDeclaration, oldToken.IsSearchable)
{
Link = link;
LeadingTrivia = oldToken.LeadingTrivia;
@@ -44,7 +48,7 @@ private Token(Token oldToken, ILink link) : this(oldToken.Document, oldToken.Ful
}
private Token(Token oldToken, ICollection leading, ICollection trailing) :
- this(oldToken.Document, oldToken.FullName, oldToken.Value, oldToken.Type, oldToken.LineNumber, oldToken.IsDeclaration)
+ this(oldToken.Document, oldToken.FullName, oldToken.Value, oldToken.Type, oldToken.LineNumber, oldToken.IsDeclaration, oldToken.IsSearchable)
{
Link = oldToken.Link;
LeadingTrivia = leading;
diff --git a/src/SourceBrowser.Generator/Model/VisualBasic/VBDelimiters.cs b/src/SourceBrowser.Generator/Model/VisualBasic/VBDelimiters.cs
new file mode 100644
index 0000000..9b081ab
--- /dev/null
+++ b/src/SourceBrowser.Generator/Model/VisualBasic/VBDelimiters.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SourceBrowser.Generator.Model.VisualBasic
+{
+ ///
+ /// Fully qualified names do not contain enough information to differentiate
+ /// between some symbols. Occasionally we must extend VB's fully qualified names
+ /// to refer to certain symbols.
+ ///
+ /// Examples: Local variables, parameters, static constructors etc.
+ ///
+ public static class VBDelimiters
+ {
+ public const string LOCAL_VARIABLE = "::";
+ public const string PARAMETER = "::";
+ }
+
+}
diff --git a/src/SourceBrowser.Generator/SourceBrowser.Generator.csproj b/src/SourceBrowser.Generator/SourceBrowser.Generator.csproj
index 3b23cc2..6d88e7b 100644
--- a/src/SourceBrowser.Generator/SourceBrowser.Generator.csproj
+++ b/src/SourceBrowser.Generator/SourceBrowser.Generator.csproj
@@ -92,6 +92,8 @@
+
+
diff --git a/src/SourceBrowser.Generator/Transformers/SearchIndexTransformer.cs b/src/SourceBrowser.Generator/Transformers/SearchIndexTransformer.cs
index ded51be..653a7d1 100644
--- a/src/SourceBrowser.Generator/Transformers/SearchIndexTransformer.cs
+++ b/src/SourceBrowser.Generator/Transformers/SearchIndexTransformer.cs
@@ -23,7 +23,7 @@ public SearchIndexTransformer(string username, string repository)
protected override void VisitDocument(DocumentModel documentModel)
{
var documentId = Path.Combine(_username, _repository, documentModel.RelativePath);
- var declarations = documentModel.Tokens.Where(n => n.IsDeclaration);
+ var declarations = documentModel.Tokens.Where(n => n.IsDeclaration && n.IsSearchable);
foreach(var declaration in declarations)
{