zoukankan      html  css  js  c++  java
  • 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。

  • 相关阅读:
    CentOS7.6配置ip
    查看当前用户下没有主键也没有唯一性索引的表
    C++ 开机自动启动
    C++ 判断是文件还是文件夹
    Duilib热键
    c++将字符转换成字符串
    duilib中各控件响应的消息类型
    Duilib程序添加托盘图标显示
    C++打开文件夹
    C++获取驱动盘句柄
  • 原文地址:https://www.cnblogs.com/P_Chou/p/1738935.html
Copyright © 2011-2022 走看看