なつねこメモ

主にプログラミング関連のメモ帳 ♪(✿╹ヮ╹)ノ 書いてあるコードは自己責任でご自由にどうぞ。記事本文の無断転載は禁止です。

Visual Studio の拡張機能を作成する - 言語サービスの提供編

前回の準備編の続きです。
今回は、言語サービスの拡張を作るための、ベース部分を作成していきます。


ということで、早速ベース部分を作成していきます。

前回作成した VSPackage を変更します。
言語サービスの提供には、 ProvideServiceAttribute, ProvideLanguageServiceAttribute を、
拡張子の登録として、 ProvideLanguageExtensionAttribute を付けます。

つけた例が下の感じ。

using System;
using System.ComponentModel.Design;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.InteropServices;

using HSPToolsVS.Language;

using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Package;
using Microsoft.VisualStudio.Shell;

namespace HSPToolsVS
{
    /// <summary>
    ///     This is the class that implements the package exposed by this assembly.
    /// </summary>
    /// <remarks>
    ///     <para>
    ///         The minimum requirement for a class to be considered a valid package for Visual Studio
    ///         is to implement the IVsPackage interface and register itself with the shell.
    ///         This package uses the helper classes defined inside the Managed Package Framework (MPF)
    ///         to do it: it derives from the Package class that provides the implementation of the
    ///         IVsPackage interface and uses the registration attributes defined in the framework to
    ///         register itself and its components with the shell. These attributes tell the pkgdef creation
    ///         utility what data to put into .pkgdef file.
    ///     </para>
    ///     <para>
    ///         To get loaded into VS, the package must be referred by &lt;Asset Type="Microsoft.VisualStudio.VsPackage" ...
    ///         &gt; in .vsixmanifest file.
    ///     </para>
    /// </remarks>
    [PackageRegistration(UseManagedResourcesOnly = true)]
    [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)] // Info on this package for Help/About
    [Guid(PackageGuidString)]
    [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly",
        Justification = "pkgdef, VS and vsixmanifest are valid VS terms")]
    // Register a Language Service
    [ProvideService(typeof(HSPLanguageService), ServiceName = "HSP Language Service")]
    [ProvideLanguageService(typeof(HSPLanguageService), "HSP", 0)]
    [ProvideLanguageExtension(typeof(HSPLanguageService), ".hsp")]
    [ProvideLanguageExtension(typeof(HSPLanguageService), ".as")]
    public sealed class HSPVSPackage : Package
    {
        /// <summary>
        ///     HSPVSPackage GUID string.
        /// </summary>
        public const string PackageGuidString = "d831da21-d940-4ad0-aa2a-4a7a04e3d6c4";

        #region Package Members

        /// <summary>
        ///     Initialization of the package; this method is called right after the package is sited, so this is the place
        ///     where you can put all the initialization code that rely on services provided by VisualStudio.
        /// </summary>
        protected override void Initialize()
        {
            base.Initialize();
        }

        protected override void Dispose(bool disposing)
        {
            var serviceContainer = this as IServiceContainer;
            serviceContainer.RemoveService(typeof(HSPLanguageService));
            base.Dispose(disposing);
        }

        #endregion
    }
}

ProvideLanguageExtension にて、 *.hsp, .as を対象の拡張子として登録しています。
また、言語サービスのクラスとして、 HSPLanguageService を指定しています。

次に、サービスコンテナに HSPLanguageService を登録します。
Initialize メソッドの中に、以下の処理を追加します。

base.Initialize();

var serviceContainer = this as IServiceContainer;
var hspLangService = new HSPLanguageService();
hspLangService.SetSite(this);
serviceContainer.AddService(typeof(HSPLanguageService), hspLangService, true);

次は、 HSPLanguageServie の実装です。
このクラスは、 LanguageService を継承しておく必要があります。

using Microsoft.VisualStudio.Package;
using Microsoft.VisualStudio.TextManager.Interop;

namespace HSPToolsVS.Language
{
    internal class HSPLanguageService : LanguageService
    {
        private LanguagePreferences _languagePreferences;
        private IScanner _scanner;

        public override string Name => "HSP";

        public override LanguagePreferences GetLanguagePreferences()
        {
            if (_languagePreferences != null)
                return _languagePreferences;
            _languagePreferences = new LanguagePreferences(Site, typeof(HSPLanguageService).GUID, Name);
            _languagePreferences.Init();
            return _languagePreferences;
        }

        public override IScanner GetScanner(IVsTextLines buffer)
        {
            return _scanner ?? (_scanner = new HSPScanner(buffer));
        }

        public override AuthoringScope ParseSource(ParseRequest req)
        {
            return new HSPAuthoringScope();
        }

        public override string GetFormatFilterList() => "HSP Script Files(*.hsp,*.as)|*.hsp;*.as";
    }
}

Name プロパティには言語名を、 GetFormatFilterList メソッドには、 ファイルダイアログで用いるフィルター文字列を設定しておきます。
フィルター文字列については、こちらを参照してください。

LanguagePreferences には、提供する機能や、言語のデフォルト設定(タブのスペース数など)を
設定したものを返します。
特に設定する必要が無い場合は、デフォルトの物を使えば問題ありません。

GetScanner 及び ParseSource は、それぞれ IScanner, AuthoringScope を実装/継承した
クラスを作成し、それを返せば OK です。

これで、言語サービスを提供するための、必要最低限の実装が完了しました。
次回は、必要不可欠な要素の一つである、シンタックスハイライトの実装をしていきます。


参考: