2016-11-14 1 views
2

は、私が効率的に次のメソッドを実装するために探しています:Roslyn:単一のソース行に正確なトークン+トリビアスパンを列挙しますか?

IEnumerable<ColoredSpan> GetSyntaxHighlightedSpansOnLine(int lineNumber); 

私はDocumentSourceTextSyntaxTreeら持っています。 ColoredSpanは、ある色と文字列のタプル(またはその他のソースchar)であるとします。例えば、このコードの3行の場合:

" ", "which continues here... */", " ", "class", " ", "Bar", " ", 
":", " ", "public", " ", "IBaz", " ", "// TODO: rename classes", "\r\n" 

注空白やコメントトリビアを含めると、部分的な複数行コメント:

namespace Foo 
{ /* Badly formatted coment... 
    which continues here... */ class Bar : public IBaz // TODO: rename classes 
    { 
     ... 

私はテキストを列挙結果を提供するために探しています。

Another answerは、ASTの全部分を歩くためにCSharpSyntaxWalkerを導出する手段を指しますが、有効なトラバーサルを1行のノードに限定することはできません。行ごとにこれは効率的ではなく、私は容易にどのサブセクションをどのように動かすことができませんでした。返信するRoslyn "trivia"(複数行コメントなど)また、重複するノード(例えば名前空間)を返します。

私は、ラを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のように保存する必要がありますか?

答えて

3

あなたはASTからこのデータを再構築することができるかもしれませんが、このためのより良いAPIはMicrosoft.CodeAnalysis.Classification.Classifierという形で利用できるようです。それlooks expensive、しかし:あなたは、あなたが彼らのGetSemanticModel()メソッドを呼び出すことにより、DocumentまたはCompilationから取り出すことができ、ハイライト表示されているソースコードのためのロスリンSemanticModelを必要とし、同期の結果を得るために

SyntaxTreeSourceTextを取得すると同時に、つまりドキュメントを取得するとすぐにこれを取得してキャッシュすることができます。 Workspaceも必要です。これらを考えると、あなたはオンデマンドでClassifier.GetClassifiedSpans()に電話することができます。

SemanticModelをすぐに入手できない場合は、代わりにClassifier.GetClassifiedSpansAsync()を呼び出して、特定のTextSpanのモデルを構築してください。

どちらの亜種でも、ほとんどあなたが求める熱量値が得られますが、それほどではありません。

まず、スパンごとに弱い型の分類(クラス名、キーワード、演算子など)を文字列 "enum"の形で返します。これらはClassificationTypeNamesクラスのconstメンバーに対応するように見えるので、信頼できるものと思われます。 ClassificationTypeNames.ClassNameらを色に簡単にマッピングできます。

は第二に、この呼び出し以降に戻りのみは、例えば、空白、ため国連分類スパンが失われますまたがる分類。このコードは(ご質問のように)ColoredSpanの存在を前提とし

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)); 
    } 
} 

と色にClassificationTypeNamesをマップStyleFromClassificationType()ヘルパー:あなたは退屈な場合は簡単です、このようなトリビアを含むスパンのフルセットを、再構築する必要があります。

Roslynには、現時点でAPIのドキュメントがないため、APIの作成者の意図を伝える場合がありますので、vimとvigorでこの実装を使用する前にパフォーマンスを測定することをおすすめします。

プロファイリングは、これは過度に高価であることを示した場合、この形式でN最近見たソース線の表現をキャッシュすることは比較的些細なことであろう、そして必要な場合/ IFときに、ソース・コードの変更そのキャッシュを無効化、再計算します。

+1

ここに追加する必要があるドキュメントはっきりしていません。このコードはVisual Studioで使用するのと同じコードですが、その上にキャッシングがあります。キャッシングに関する質問は難しく、正直なところシナリオを知らなくても答えにくいです。 –

+0

@JasonMalinowskiこれは、最高の安心です、ありがとう - 少なくともここで正しい行に。私たちは素晴らしいRoslynコードを見ることができますが、コメントはほとんどありませんので、実装か​​らAPI契約を推測するのは難しいので、誤ったツリー/将来の自己のために厄介なパフォーマンスの問題を作り出します。ちなみに、Roslyn overview wikiのシンタックスハイライトとクラシファイアの言及を見てうれしいです。しかし、私はGithubで本当にそれを上げるべきです。 –

関連する問題