Vamos a buscar // TODO: ... con Roslyn
Para empezar en una aplicación de consola simple agreguemos algunos paquetes. Microsoft.CodeAnalysis.CSharp y Microsoft.CodeAnalysis.CSharp.Workspaces son lo que necesitamos.
Y hacemos esto:
const int ExitOK = 0;
const int ExitError = 99;
const int ExitIssueFound = 1;
static async Task<int> MainAsync(string[] args)
{
var workspace = await GetWorkspace().ConfigureAwait(false);
if (workspace == null)
return ExitError;
using (workspace)
{
var issueFound = false;
foreach (var project in workspace.CurrentSolution.Projects)
{
foreach (var document in project.Documents)
{
var documentWritten = false;
var root = await document.GetSyntaxRootAsync().ConfigureAwait(false);
foreach (var item in root.DescendantTrivia().Where(x => x.IsKind(SyntaxKind.SingleLineCommentTrivia)))
{
var match = Regex.Match(item.ToFullString(), @"//\s?TODO:\s*(.*)");
if (match.Success)
{
issueFound = true;
var text = match.Groups[1].Value;
if (!documentWritten)
{
documentWritten = true;
Console.WriteLine(MinimizePath(document.FilePath));
}
var position = item.GetLocation().GetMappedLineSpan();
var line = position.StartLinePosition.Line;
Console.WriteLine($"\tL{line}:\t{text}");
}
}
}
}
return issueFound ? ExitIssueFound : ExitOK;
}
}
static async Task<Workspace> GetWorkspace()
{
var workspace = MSBuildWorkspace.Create();
var solution = Directory.EnumerateFiles(Environment.CurrentDirectory, "*.sln", SearchOption.TopDirectoryOnly).FirstOrDefault();
if (solution != null)
{
await workspace.OpenSolutionAsync(solution).ConfigureAwait(false);
return workspace;
}
var project = Directory.EnumerateFiles(Environment.CurrentDirectory, "*.csproj", SearchOption.TopDirectoryOnly).FirstOrDefault();
if (project != null)
{
await workspace.OpenProjectAsync(project).ConfigureAwait(false);
return workspace;
}
return null;
}
static string MinimizePath(string path)
{
return path.Remove(0, Environment.CurrentDirectory.Length + 1);
}