如何在VS宏中检索标识符的完全限定名称? [英] How would I retrieve the fully qualified name of an identifier in a VS macro?

查看:78
本文介绍了如何在VS宏中检索标识符的完全限定名称?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用Visual Studio 2008中的宏(甚至是外接程序)在代码窗口的特定点(光标)处解析c#标识符的全限定名称.

I'm trying to resolve the fully qualified name of a c# identifier at a certain point (cursor) of a code window, using a Macro (or even an Add-in) in Visual Studio 2008.

例如,如果光标位于矩形"中,我希望返回"System.Drawing.Rectangle".

For example, if the cursor is in "Rectangle", I would like "System.Drawing.Rectangle" returned.

我尝试了FileCodeModel.CodeElements.CodeElementFromPoint,但是它们仅检索包含的方法或类(以及其他).

I've tried FileCodeModel.CodeElements and .CodeElementFromPoint but they only retrieve the containing method or class (and others).

如果使用宏或外接程序无法做到这一点(即使VS通过intellisense知道信息),是否可以在c#文件中使用Reflection读取并获得所需的信息?

If this can't be done using a macro or add-in (even though VS does know the information via intellisense), would it be possible to use Reflection read in the c# file and get the desired info?

推荐答案

可以完成.这是一个解决方案(尽管有些古怪):使用F1帮助上下文.为了使F1帮助起作用,Visual Studio将当前选择或插入点的标准类型名称推入称为"F1帮助上下文"的名称/值对包中.而且,Visual Studio SDK中提供了一些公共API,用于查询F1帮助上下文的内容.

It can be done. Here's one solution (albeit a somewhat hacky one): use F1 Help Context. In order to make F1 help work, Visual Studio pushes the fully-qualified type name of the current selection or insertion point into a bag of name/value pairs called "F1 Help Context". And there are public APIs in the Visual Studio SDK for querying the contents of F1 Help Context.

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

In order to stay sane, you'll want to enable the debugging registry key for F1 Help Context. This lets you see what's in Help Context at any time via the oft-maligned Dynamic Help window. To do this:

  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"

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

As you'll see from looking at the F1 debug output, Visual Studio doesn't explicitly tell you "this is the identifier's type". Instead, it simply sticks the fully-qualified type name at the head of one or more "Help Keywords" which F1 uses to bring up help. For example, you can have System.String, VS.TextEditor, and VS.Ambient in your help context, and only the first one is related to the current code.

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

The trick to make this easier is this: Visual Studio can mark keywords as case-sensitive or case-insensitive. AFAIK, the only part of Visual Studio which injects case-sensitive keywords is the code editor of case-sensitive languages (C#, C++) in response to code context. Therefore, if you filter all keywords to case-sensitive keywords, you know you're looking at code.

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

Unfortunately, the C# editor also pushes language keywords (not just identifiers) into help context if the insertion point is on top of a language keyword. So you'll need to screen out language keywords. There are two ways to do this. You can simply try to look them up in the type system, and since they're not valid type names (especially not the way VS mangles them, e.g. "string_CSharpKeyword" for the string keyword) you can just fail silently. Or you can detect the lack of dots and assume it's not a type name. Or you can detect the _CSharpKeyword suffix and hope the VS team doesn't change it. :-)

另一个潜在的问题是泛型.您将从VS获得的泛型类型名称看起来像这样:

Another potential issue is generics. The type name you'll get from VS for a generic type looks like this:

System.Collections.Generic.List`1 

和方法如下:

System.Collections.Generic.List`1.FindAll.

您需要在检测反-音并处理它时要非常聪明.

You'll need to be smart about detecting the back-tick and dealing with it.

此外,在诸如ASP.NET MVC .ASPX文件的情况下,如果页面上同时包含C#代码和其他区分大小写的代码(例如javascript),您可能会得到有趣的行为.在这种情况下,您还需要查看属性.除关键字外,帮助上下文还具有属性",它们是描述当前上下文的名称/值对.例如,devlang = csharp是一个属性.下面的代码也可以用于提取属性.您将需要进行实验以找出要查找的正确属性,以免最终无法使用javascript或其他奇数代码.

Also, you might get interesting behavior in cases like ASP.NET MVC .ASPX files where there's both C# code and other case-sensitive code (e.g. javascript) on the page. In that case, you'll need to look at the attributes as well. In addition to keywords, Help Context also has "attributes", which are name/value pairs describing the current context. For example, devlang=csharp is one attribute. The code below can be used to pull out attributes too. You'll need to experiment to figure out the right attributes to look for so you don't end up acting on javascript or other odd code.

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

Anyway, now that you understand (or at least have been exposed to!) all the caveats, here's some code to pull out the case-sensitive keyword (if it exists) from help context, as well as the rest of the name/value pairs. (keywords are simply name/value pairs whose name is "keyword").

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

Keep in mind that this code requires the Visual Studio SDK (not just the regular VS install) in order to build, in order to get the Microsoft.VisualStudio.Shell.Interop, Microsoft.VisualStudio.Shell, and Microsoft.VisualStudio.OLE.Interop namespaces (which you'll need to add as references in your addin project).

好,祝你好运!

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");
        }
    }
}

这篇关于如何在VS宏中检索标识符的完全限定名称?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆