本筋からはちょっとズレますが、あると嬉しい機能である、ツールチップ表示を実装します。
画像のように、テキスト上にカーソルを持って行くと表示されるあれです。
ツールチップを実装するには、とりあえず継承しただけである AuthoringScope
クラス内の、
GetDataTipText
を使用します。
GetDataTipText
には、 Ln, Col が渡されるので、該当位置にあるトークンを取得し、
それにあった結果を返します。
しかしながら、コードの解析を行う IScanner
には行番号が降ってこないため、
行番号が渡されるよう、次のように変更します。
まず、 Microsoft.VisualStudio.Package.Colorizer
を継承したクラスを作成します。
次に、 ColorizeLine
及び GetLineInfo
をオーバーライドし、Scanner
へと
行番号を渡すように変更します。
これらのメソッドは、内部で IScanner
の SetSource
及び ScanToken~
を呼び出しており、
また、 SetSource
及び ScanToken~
はこれらのメソッド経由で呼び出されています。
そのため、その処理の直前で行番号を渡します。
これらを実装したものが下のコード。
using System; using System.Runtime.InteropServices; using Microsoft.VisualStudio.Package; using Microsoft.VisualStudio.TextManager.Interop; using VSLanguageService = Microsoft.VisualStudio.Package.LanguageService; namespace HSPToolsVS.LanguageService { [ComVisible(true)] internal class HSPColorizer : Colorizer { public HSPColorizer(VSLanguageService svc, IVsTextLines buffer, IScanner scanner) : base(svc, buffer, scanner) { } #region Overrides of Colorizer public override int ColorizeLine(int line, int length, IntPtr ptr, int state, uint[] attrs) { (Scanner as HSPScanner)?.SetLineNumber(line); return base.ColorizeLine(line, length, ptr, state, attrs); } public override TokenInfo[] GetLineInfo(IVsTextLines buffer, int line, IVsTextColorState colorState) { (Scanner as HSPScanner)?.SetLineNumber(line); return base.GetLineInfo(buffer, line, colorState); } #endregion } }
次に、上の Colorizer を使うように、 HSPLanguageService
を変更します。
public override Colorizer GetColorizer(IVsTextLines buffer) { return new HSPColorizer(this, buffer, GetScanner(buffer)); }
なお、 GetColorizer
はキャッシュ機構を持っているため、
本来ならばそれも実装すべきかもしれません。
あとは、 AuthoringScope
にて、トークンを元にツールチップテキストを返します。
using System.Collections.Generic; using System.Linq; using Microsoft.VisualStudio; using Microsoft.VisualStudio.Package; using Microsoft.VisualStudio.TextManager.Interop; namespace HSPToolsVS.LanguageService { internal class HSPAuthoringScope : AuthoringScope { private readonly IEnumerable<Token> _tokens; public HSPAuthoringScope(IEnumerable<Token> tokens) { _tokens = tokens; } public override string GetDataTipText(int line, int col, out TextSpan span) { span = new TextSpan(); var token = GetTokenFromLineAndCol(line, col); if (token == null || token.Type == HSPTokenType.Comment || token.Type == HSPTokenType.Numeric) return null; span.iStartLine = line; span.iEndLine = line; span.iStartIndex = token.StartIndex; span.iEndIndex = token.EndIndex; return $"{token.Type} : {token.Text}"; } // 略 private Token GetTokenFromLineAndCol(int line, int col) { return _tokens.SingleOrDefault(w => w.Line == line && w.StartIndex <= col && col <= w.EndIndex); } } }
参考: