gpt4 book ai didi

c# - Roslyn:在单个源代码行中枚举确切的标记 + 琐事跨度?

转载 作者:行者123 更新时间:2023-11-30 21:44:52 25 4
gpt4 key购买 nike

我希望有效地实现以下方法:

IEnumerable<ColoredSpan> GetSyntaxHighlightedSpansOnLine(int lineNumber);

我有一个 DocumentSourceTextSyntaxTree 等。假设 ColoredSpan 是一些颜色和字符串(或 char 的其他来源)的元组。例如这段代码的第三行:

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 部分的方法,但不能有效地将遍历限制为单行节点。在每行的基础上,这效率不高,我无法轻易找出例如哪些小节。罗斯林“琐事”(例如多行评论)返回。它还返回重叠节点(例如 namespace )。

我试过了code as in this answer , 啦:

var lineSpan = sf.GetText().Lines[lineNumber].Span;
var nodes = syntaxTree.GetRoot()
.DescendantNodes()
.Where(x => x.Span.IntersectsWith(lineSpan))

但这会返回整个 AST 子树,前序遍历,这同样是低效的,并且还会返回重叠节点(例如命名空间)并且不处理琐事。其他示例适用于整个文档/脚本。我还查阅了接近零的 API 文档。

代码分析 API 是否有效地允许这样做?或者为了实现该方法,我是否需要提前遍历整个 AST 并存储我自己设计的主观庞大的并行内存消耗数据结构,如 this answer

最佳答案

虽然您可以从 AST 重建此数据,但似乎可以 Microsoft.CodeAnalysis.Classification.Classifier 的形式提供更好的 API。它looks expensive , 然而:

对于同步结果,您需要为要突出显示的源代码使用 Roslyn SemanticModel,您可以从 DocumentCompilation 中获取它通过调用他们的 GetSemanticModel() 方法。您可以在获取 SyntaxTreeSourceText 的同时获取和缓存它,即一旦您拥有文档。您还需要一个 Workspace。鉴于这些,您可以调用 Classifier.GetClassifiedSpans()按需。

如果您不能轻易获得当前的SemanticModel,您可以改为调用Classifier.GetClassifiedSpansAsync()。这将为您构建特定 TextSpan 的微型模型。

这两种变体都能为您提供几乎您所要求的枚举,但不完全是。

首先,它以字符串“enum”的形式返回每个span的弱类型分类(类名、关键字、运算符等);这些似乎对应于 ClassificationTypeNames 的 const 成员类,所以大概他们是可靠的。您可以简单地将 ClassificationTypeNames.ClassName 等映射到颜色。

其次,由于此调用仅返回分类 跨度,因此将缺少分类跨度,例如空白。您将不得不重建包括此类琐事在内的全套跨度,这虽然乏味但很简单:

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() 帮助器,它将 ClassificationTypeNames 映射到颜色。

由于 Roslyn 目前缺少任何可能传达作者对这些 API 的意图的 API 文档,我建议在使用 vim 和 vigor 实现之前先测量性能。

如果分析显示这过于昂贵,那么以这种格式缓存 n 个最近查看的源代码行表示并在需要的地方重新计算,如果/当源代码时使缓存无效变化。

关于c# - Roslyn:在单个源代码行中枚举确切的标记 + 琐事跨度?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40589891/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com