如何在VS宏中检索标识符的完全限定名称?

ste*_*ste 4 c# macros intellisense visual-studio-addins visual-studio-2008

我正在尝试使用Visual Studio 2008中的宏(甚至是加载项)在代码窗口的某个点(光标)处解析ac#identifier的完全限定名称.

例如,如果光标在"Rectangle"中,我想返回"System.Drawing.Rectangle".

我已经尝试了FileCodeModel.CodeElements,.CodeElementFromPoint但他们只检索包含方法或类(和其他).

如果使用宏或加载项无法完成此操作(即使VS通过intellisense知道信息),是否可以在c#文件中使用Reflection读取并获取所需信息?

Jus*_*ant 7

可以办到.这是一个解决方案(虽然有点笨拙):使用F1帮助上下文.为了使F1帮助工作,Visual Studio将当前选择或插入点的完全限定类型名称推送到名为"F1帮助上下文"的名称/值对的包中.Visual Studio SDK中有公共API,用于查询F1帮助上下文的内容.

为了保持理智,您需要为F1帮助上下文启用调试注册表项.这使您可以通过常用的动态帮助窗口随时查看帮助上下文中的内容.去做这个:

  1. 启动visual studio并从"帮助"菜单中选择"动态帮助".
  2. 在下面设置注册表项(您需要步骤#1来创建注册表树)
  3. 重新启动Visual Studio以获取更改
  4. 现在,在动态帮助窗口中将有调试输出,以便您可以看到F1帮助上下文中的内容.本答案的其余部分描述了如何以编程方式获取该上下文,以便您的加载项可以使用它.

这是注册表项:

[HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0\Dynamic Help]
"Display Debug Output in Retail"="YES"
Run Code Online (Sandbox Code Playgroud)

正如您在查看F1调试输出时所看到的,Visual Studio没有明确告诉您"这是标识符的类型".相反,它只是将完全限定的类型名称添加到一个或多个"帮助关键字"的头部,F1用于提供帮助.例如,您可以在帮助上下文中包含System.String,VS.TextEditor和VS.Ambient,并且只有第一个与当前代码相关.

使这更容易的技巧是:Visual Studio可以将关键字标记为区分大小写或不区分大小写.AFAIK是Visual Studio中注入区分大小写关键字的唯一部分,它是区分大小写的语言(C#,C++)的代码编辑器,用于响应代码上下文.因此,如果您将所有关键字过滤为区分大小写的关键字,则表示您正在查看代码.

不幸的是,如果插入点位于语言关键字之上,C#编辑器还会将语言关键字(不仅仅是标识符)推送到帮助上下文中.因此,您需要筛选语言关键字.有两种方法可以做到这一点.您可以简单地尝试在类型系统中查找它们,并且因为它们不是有效的类型名称(特别是VS不会破坏它们的方式,例如字符串关键字的"string_CSharpKeyword"),您可以无声地失败.或者您可以检测到缺少点并假设它不是类型名称.或者您可以检测_CSharpKeyword后缀,并希望VS团队不会更改它.:-)

另一个潜在的问题是泛型.您从VS获取泛型类型的类型名称如下所示:

System.Collections.Generic.List`1 
Run Code Online (Sandbox Code Playgroud)

和方法看起来像这样:

System.Collections.Generic.List`1.FindAll.
Run Code Online (Sandbox Code Playgroud)

你需要聪明地检测后退并处理它.

此外,在ASP.NET MVC .ASPX文件的情况下,您可能会获得有趣的行为,其中包含C#代码和页面上的其他区分大小写的代码(例如javascript).在这种情况下,您还需要查看属性.除了关键字,"帮助上下文"还具有"属性",这些属性是描述当前上下文的名称/值对.例如,devlang = csharp是一个属性.下面的代码也可用于提取属性.您需要尝试找出要查找的正确属性,这样您就不会最终使用javascript或其他奇怪的代码.

无论如何,既然你理解了(或者至少已经暴露过!)所有的警告,这里有一些代码可以从帮助上下文中提取区分大小写的关键字(如果存在),以及名称/值的其余部分对.(关键字只是名称为"关键字"的名称/值对).

请记住,为了获得Microsoft.VisualStudio.Shell.Interop,Microsoft.VisualStudio.Shell和Microsoft.VisualStudio.OLE,此代码需要Visual Studio SDK(而不仅仅是常规的VS安装)才能构建. Interop命名空间(您需要在addin项目中添加为引用).

好的,玩得开心,祝你好运!

using System;
using Extensibility;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.CommandBars;
using System.Resources;
using System.Reflection;
using System.Globalization;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.OLE.Interop;
using System.Collections.Generic;

public class HelpAttribute
{
    public string Name;
    public string Value;
    public VSUSERCONTEXTPRIORITY Priority;
    public VSUSERCONTEXTATTRIBUTEUSAGE Usage;
}

public class HelpContext2 : List<HelpAttribute>
{
    public static HelpContext2 GetHelpContext(DTE2 dte)
    {
        // Get a reference to the current active window (presumably a code editor).
        Window activeWindow = dte.ActiveWindow;

        // make a few gnarly COM-interop calls in order to get Help Context 
        Microsoft.VisualStudio.OLE.Interop.IServiceProvider sp = (Microsoft.VisualStudio.OLE.Interop.IServiceProvider)activeWindow.DTE;
        Microsoft.VisualStudio.Shell.ServiceProvider serviceProvider = new Microsoft.VisualStudio.Shell.ServiceProvider(sp);
        IVsMonitorUserContext contextMonitor = (IVsMonitorUserContext)serviceProvider.GetService(typeof(IVsMonitorUserContext));
        IVsUserContext userContext;
        int hresult = contextMonitor.get_ApplicationContext(out userContext);
        HelpContext2 attrs = new HelpContext2(userContext);

        return attrs;
    }
    public HelpContext2(IVsUserContext userContext)
    {
        int count;
        userContext.CountAttributes(null, 1, out count);
        for (int i = 0; i < count; i++)
        {
            string name, value;
            int priority;
            userContext.GetAttributePri(i, null, 1, out priority, out name, out value);
            VSUSERCONTEXTATTRIBUTEUSAGE[] usageArray = new VSUSERCONTEXTATTRIBUTEUSAGE[1];
            userContext.GetAttrUsage(i, 1, usageArray);
            VSUSERCONTEXTATTRIBUTEUSAGE usage = usageArray[0];
            HelpAttribute attr = new HelpAttribute();
            attr.Name = name;
            attr.Value = value;
            attr.Priority = (VSUSERCONTEXTPRIORITY)priority;
            attr.Usage = usage; // name == "keyword" ? VSUSERCONTEXTATTRIBUTEUSAGE.VSUC_Usage_Lookup : VSUSERCONTEXTATTRIBUTEUSAGE.VSUC_Usage_Filter;
            this.Add(attr);
        }
    }
    public string CaseSensitiveKeyword
    {
        get
        {
            HelpAttribute caseSensitive = Keywords.Find(attr => 
                attr.Usage == VSUSERCONTEXTATTRIBUTEUSAGE.VSUC_Usage_LookupF1_CaseSensitive
                || attr.Usage == VSUSERCONTEXTATTRIBUTEUSAGE.VSUC_Usage_Lookup_CaseSensitive
                );
            return caseSensitive == null ? null : caseSensitive.Value;
        }
    }
    public List<HelpAttribute> Keywords
    {
        get
        {
            return this.FindAll(attr=> attr.Name == "keyword");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)