我希望有效地实现以下方法:
IEnumerable<ColoredSpan> GetSyntaxHighlightedSpansOnLine(int lineNumber);
我有Document
,SourceText
,SyntaxTree
等。假设ColoredSpan
是一些颜色和字符串(或char
s的其他来源)的元组。例如,对于此代码的第三行:
namespace Foo
{ /* Badly formatted coment...
which continues here... */ class Bar : public IBaz // TODO: rename classes
{
...
我希望用文字提供可枚举的结果:
" ", "which continues here... */", " ", "class", " ", "Bar", " ",
":", " ", "public", " ", "IBaz", " ", "// TODO: rename classes", "\r\n"
请注意包含空格和评论琐事以及部分多行注释。
Another answer指向导出CSharpSyntaxWalker以遍历AST的整个部分的方法,但不是为了有效地限制遍历到单个线路的节点。在每行的基础上,这是没有效率的,我不能轻易地确定哪个子部分例如Roslyn“琐事”(例如多行评论)返回。它还返回重叠的节点(例如名称空间)。
我试过code as in this answer,la:
var lineSpan = sf.GetText().Lines[lineNumber].Span;
var nodes = syntaxTree.GetRoot()
.DescendantNodes()
.Where(x => x.Span.IntersectsWith(lineSpan))
但是这会返回整个AST子树,preorder遍历,这也是效率低下的,并且还返回重叠节点(例如名称空间)并且不处理琐事。其他示例适用于整个文档/脚本。我还查阅了零文档旁边的API文档。
代码分析API是否有效地允许此操作?或者为了实现这个方法,我是否需要提前遍历整个AST并存储一个主观庞大的并行内存消耗数据结构,如我自己设计的this answer?
答案 0 :(得分:3)
虽然您可以从AST重建此数据,但是更好的API似乎以&&
的形式提供。但是looks expensive:
对于同步结果,您需要一个Roslyn Microsoft.CodeAnalysis.Classification.Classifier
作为您要突出显示的源代码,您可以通过调用SemanticModel
方法从Document
或Compilation
获取该源代码。您可以在获取GetSemanticModel()
和SyntaxTree
的同时获取并缓存此内容,即只要您拥有该文档即可。您还需要SourceText
。鉴于这些,您可以按需拨打Classifier.GetClassifiedSpans()
。
如果您无法轻易获得当前Workspace
,则可以打电话给Classifier.GetClassifiedSpansAsync()
,这将为您构建特定SemanticModel
的微型模型。
这两种变体都为你提供了几乎所要求的数量,但并不完全。
首先,它以字符串“enum”的形式为每个跨度返回弱类型分类(类名,关键字,运算符等);这些似乎对应ClassificationTypeNames
类的const成员,因此可能它们是可靠的。您可以简单地将ClassificationTypeNames.ClassName等映射到颜色。
其次,由于此调用仅返回分类跨度,因此将缺少 un 分类的跨度,例如,空格。你将不得不重建完整的跨度,包括这样的琐事,如果单调乏味,这很简单:
TextSpan
此代码假设存在IEnumerable<ColoredSpan> DescribeLine(int lineNumber)
{
var lineSpan = sourceText.Lines[lineNumber].Span;
var classified = Classifier.GetClassifiedSpans(semanticModel, lineSpan, workspace);
var cursor = lineSpan.Start;
// Presuming you need a string rather than a TextSpan.
Func<TextSpan, string> textOf = x => sourceText.ToString(x);
if (!classified.Any())
yield return new ColoredSpan(defaultStyle, textOf(lineSpan));
foreach (var overlap in classified)
{
var classified = overlap.TextSpan.Intersection(lineSpan).Value;
if (classified.Start > cursor)
{
var unclassified = new TextSpan(cursor, classified.Start - cursor);
cursor = classified.Start;
yield return new ColoredSpan(defaultStyle, textOf(unclassified));
}
var style = StyleFromClassificationType(overlapping.ClassificationType);
yield return new ColoredSpan(style, textOf((TextSpan)classified));
cursor = classified.Start + classified.Length;
}
if (cursor < lineSpan.Start + lineSpan.Length)
{
var trailing = new TextSpan(cursor, lineSpan.Start + lineSpan.Length - cursor);
yield return new ColoredSpan(defaultStyle, textOf(trailing));
}
}
(如您的问题所示)和ColoredSpan
帮助程序,它将StyleFromClassificationType()
映射到颜色。
由于Roslyn目前缺少任何API文档,这可能会传达作者对这些API的意图,因此我建议在使用vim和vigor的实现之前测量性能。
如果分析显示这非常昂贵,那么以这种格式缓存 n 最近查看的源代码行表示会相对微不足道,并且在需要的地方重新计算,如果/当源代码时使该缓存无效变化。