How to get full identifier name in VS macro?

I am trying to resolve the C # identifier full name at a specific point (cursor) in the code window using a macro (or even an add-in) in Visual Studio 2008.

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

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

If this cannot be done with a macro or add-in (even if VS knows the information through intellisense), is it possible to use Reflection to read in a C # file and get the right information?

+4
source share
1 answer

It can be done. Here's one solution (albeit somewhat hacky): use the F1 context context. To help F1 cope, Visual Studio pushes a fully qualified name such as the current selection or insertion point in the bag of name / value pairs called the "F1 Help Context". The Visual Studio SDK has public APIs for querying the contents of the F1 context menu.

In order to keep working, you need to enable the registry key for debugging for the F1 context menu. This allows you to see that in the help context you can at any time through the Ophthalmology dynamic help window. For this:

  • start the visual studio and select "Dynamic Help" in the "Help" menu.
  • install the registry key below (you need to take step # 1 to create a registry tree)
  • restart Visual Studio to get the changes.
  • Now the debug output will be displayed in the dynamic help window so that you can see what is in the context of the F1 help. The rest of this answer describes how to get this context programmatically so your add-in can use it.

Here's the registry key:

 [HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\9.0\Dynamic Help] "Display Debug Output in Retail"="YES" 

As you will see, looking at the debug output of F1, Visual Studio does not explicitly tell you that "this is an identifier type." Instead, it simply adheres to a fully qualified type name at the head of one or more of the “help keywords” that F1 uses to invoke the help. For example, you can have System.String, VS.TextEditor and VS.Amient in the help context, and only the first one is related to the current code.

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

Unfortunately, the C # editor also pushes language keywords (and not just identifiers) into the help context if the entry point is on top of the language keyword. Thus, you will need to disable language keywords. There are two ways to do this. You can simply try to find them in the type system, and since they are not valid type names (especially not because VS manages them, for example, "string_CSharpKeyword" for a string keyword), you can simply fail. Or you may discover the absence of dots and not assume that it is not a type name. Or you can define the suffix _CSharpKeyword and hope that the VS command does not change it. :-)

Another potential problem is generics. The type name that you get from VS for the generic type is as follows:

 System.Collections.Generic.List`1 

and the methods are as follows:

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

You need to be smart in detecting and reversing a tick.

In addition, you can get interesting behavior in such cases as ASP.NET MVC.ASPX files, where the page contains both C # code and other case-sensitive code (for example, javascript). In this case, you will also need attributes. In addition to keywords, the help context also has “attributes”, which are name / value pairs that describe the current context. For example, devlang = csharp is a single attribute. The code below can be used to pull attributes. You will need to experiment to determine the correct attributes, to look for you to not take effect with javascript or other odd code.

In any case, now that you understand (or at least have been exposed!) All the warnings, here is some code to get the case-sensitive keyword (if it exists) from the help context, as well as the rest of the name / pairs value. (keywords are name and value pairs whose name is “keyword”).

Keep in mind that to create this code to create Microsoft.VisualStudio.Shell.Interop, Microsoft.VisualStudio.Shell and Microsoft.VisualStudio you need this Visual Studio SDK code (not just a regular VS installation) .. OLE.Interop namespaces (which you need to add as links to the addin project).

Ok, have fun and good luck!

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

All Articles