VSX开发之语言服务系列(4)——从空Package开始构建语言服务框架

回顾

在前面两篇中,我们研究了ManagedMyC这个例子的代码结构,了解了一个语言服务基本组成以及Managed Babel在语言服务中是如何工作的。在接下来的两篇中,我将从一个空的集成模式Package开始,一步步创建一个简单的语言服务:在这个语言中,没有语法,我们只是简单的利用着色器区分数字和字母,该服务定义的文件后缀为".sls"。

逐步开始

一、创建空Package

语言服务的载体是包,因此我们需要创建一个空的Package来承载。创建空包可以通过向导完成,这里不再累述,读者可参考:LearnVSXNow!-#2 创建一个空的VS Package。我将命名这个包为SimpleLSHost

二、代码结构的建立

1.仿照ManagedMyC为工程建立相应的目录结构,这有助于更好的管理代码,可以像这样建立:

image 2.添加Babel的代码。定位到C:\Program Files\Microsoft Visual Studio 2008 SDK\VisualStudioIntegration\Common\Source\CSharp\Babel,可以看到Babel的代码文件。如果你SDK的安装路径与我不同,你需要自己查找。将这些cs文件全部拷贝到当前工程下的ManagedBabel文件夹内,然后在工程中的ManagedBabel下添加这些.cs文件。之所以这样做,是因为根据我的经验,Babel的代码往往是要更改的,所以我们干脆拷贝一份出来。

image

3.在UserSupplied目录下添加两个.cs:Configuration.csLanguageService.cs,把SimpleLSHostPackage.cs转移至UserSupplied下。

4.找到ManagedMyC所在目录,我的是:...\AppData\Local\VSSDK_9.0_1.1\Example.ManagedMyC_F3D35F91\,把其中Generated里面的文件拷贝到我们自己的Generated里面,并把他们添加到工程中的Generated中。完成以上步骤后,目录结构如下:

image

三、快速建立的语言服务

打开UserSupplied\LanguageService.cs,添加如下代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace Company.SimpleLSHost.UserSupplied
{
    [Guid("EDA0749C-7AA5-43b8-97CC-FBD21040AD7A")]
    class LanguageService : Babel.BabelLanguageService
    {
        public override string GetFormatFilterList()
        {
            return "Sls File (*.sls)\n*.sls";
        }
    }
}

这里的代码便是我们的LS类,可以看到我们继承了Babel.BabelLanguageService,并且给这个服务指定了一个GUID,可以通过IDE的Tool->Create GUID这个工具来随即生成一个GUID:

image 四、为Package添加注册信息

SimpleLSHostPackage加上如下属性定义,并继承自Babel.BabelPackage,删除自动生成的构造函数和初始化函数。

    [ProvideService(typeof(Company.SimpleLSHost.UserSupplied.LanguageService))]
    [ProvideLanguageExtension(typeof(Company.SimpleLSHost.UserSupplied.LanguageService), ".sls")]
    [ProvideLanguageService(typeof(Company.SimpleLSHost.UserSupplied.LanguageService), "Simple LS", 0,
     CodeSense = true,
     EnableCommenting = true,
     MatchBraces = true,
     ShowCompletion = true,
     ShowMatchingBrace = true,
     AutoOutlining = true,
     EnableAsyncCompletion = true,
     CodeSenseDelay = 0)]
    [Guid(GuidList.guidSimpleLSHostPkgString)]
五、补充程序集引用

添加如下两个程序集:Microsoft.VisualStudio.Package.LanguageService.9.0、Microsoft.VisualStudio.TextManager.Interop.8.0

六、编辑工程设置

之前我们提到过lexer.lex和parser.y。这两种格式是微软定义的。用户分别用lex和yacc语法的特殊语言来编辑这两个文件。这两者的鼻祖就是lex和yacc,最初出现在Unix中,后来linux出现了两个工具flex和bison,它们负责将lex和yacc定义的文件编译成C代码。在VS2008SDK中,微软提供了类似的两个工具MPLexMPPG用于编译lexer.lex和parser.y(可以在SDK工具中找到它们),输出文件分别是lexer.csparser.cs,它们都是C#代码,于是它们就可以和我们的其他C#代码共同被C#编译器编译运行了。但是IDE并不认识lexer.lex和parser.y,我们需要手动为这两个文件设置编译器,然而微软在这里犯了个错误:

image

如上图,在可选的编译行为中,我们找不到MPLexCompileMPPGCompile,而且手动输入是无效的。因此,我们只能手动修改工程的配置文件:保存工程,右击SimpleLSHost工程,选择Unload Project,再次右击工程,选择Edit SimpleLSHost.csproj,找到如下代码段:

  <ItemGroup>
    <None Include="Generated\lexer.lex" />
    <None Include="Generated\parser.y" />
  </ItemGroup>

修改为:

  <ItemGroup>
    <MPLexCompile Include="Generated\lexer.lex" />
    <MPPGCompile Include="Generated\parser.y" />
  </ItemGroup>

保存后,右击工程,选择Reload Project。我们再看lexer.lex和parser.y的属性->Build Action,分别设置成了MPLexCompile、MPPGCompile。这样在每次编译的时候,都会使用MPLexMPPG编译它们,并且输出的文件可以在obj文件夹中看到。

七、编译查错

现在我们可以ReBuild一下,看看结果,一堆错误是吧!没关系,我们慢慢看。

最多的问题是,找不到语言服务类,这个问题就是我在上一篇中提到的BabelPackage的一个缺点造成的,BabelPackage代码指定加载的是一个Babel.LanguageService,然而我们的LanguageService是Company.SimpleLSHost.UserSupplied.LanguageService!当然找不到啦!把Babel.LanguageService改成Company.SimpleLSHost.UserSupplied.LanguageService即可。剩下的问题是:我们还没有编辑过Configuration.cs,我们也没有实现一个IASTResolver接口的Resolver。首先,在UserSupplied目录下,添加一个Resolver.cs,我们还是偷偷懒,把ManagedMyC中Resolver.cs的代码拷贝过来,注意只要拷贝类部分,名字空间不必相同,最后AuthoringScope.cs中添加名字空间,using Company.SimpleLSHost.UserSupplied。最后,将Configuration.cs中的代码用ManagedMyC的Configuration.cs的代码覆盖,注意,这里全都要拷贝,包括名字空间。编译,直到没有错误为止。一般这里如果还有错误,基本上是名字空间不对应,读者可以自己检查。

八、稍作改动

现在我们要对UserSupplied\Configuration.cs稍作改动,将下面代码覆盖原来的代码:

        public const string Name = "Simple LS";
        public const string Extension = ".sls";

然后可以用这两个常量替换package注册信息中的常量:

   [ProvideLanguageExtension(typeof(Company.SimpleLSHost.UserSupplied.LanguageService), Babel.Configuration.Extension)]
    [ProvideLanguageService(typeof(Company.SimpleLSHost.UserSupplied.LanguageService), Babel.Configuration.Name, 0,

运行调试

重新编译,并按F5运行,打开一个测试文件1.sls,结果如下:

image 可以看到,这个语言服务的结果更ManagedMyC一样。因为我们的核心lexer和parser是从ManagedMyC中拷贝来的,Configuration也是用的ManagedMyC的,当然结果一样了,唯一不同的是我们这个包注册了不同的后缀而已!

小结

本文我们从一个空的Package开始逐步构建了一个语言服务框架,并且应用了ManagedMyC的lexer和parser,作了简单的测试。本文所提到的步骤是今后构建语言服务的基础,大家也可以参考”How Do I?”视频的内容。在下一篇中,我们将在此基础上尝试修改lexer和parser,以及Configuration。

原文地址:https://www.cnblogs.com/P_Chou/p/1738935.html