How to access project code meta data?

2019-03-06 08:34发布

In my VSPackage I need to replace reference to a property in code with its actual value. For example

public static void Main(string[] args) {
    Console.WriteLine(Resource.HelloWorld);
}

What I want is to replace "Resource.HelloWorld" with its actual value - that is, find class Resource and get value of its static property HelloWorld. Does Visual Studio expose any API to handle code model of the project? It definitely has one, because this is very similar to common task of renaming variables. I don't want to use reflection on output assembly, because it's slow and it locks the file for a while.

1条回答
做个烂人
2楼-- · 2019-03-06 09:30

There is no straight forward way to do this that I know of. Reliably getting an AST out of Visual Studio (and changes to it) has always been a big problem. Part of the goal of the Rosalyn project is to create an unified way of doing this, because many tool windows had their own way of doing this sort of stuff.

There are four ways to do this:

  1. Symbols
  2. FileCodeModel + CodeDOM
  3. Rosalyn AST
  4. Unexplored Method

Symbols

I believe most tool windows such as the CodeView and things like Code Element Search use the symbols created from a compiled build. This is not ideal as it is a little more heavy weight and hard to keep in sync. You'd have to cache symbols to make this not slow. Using reflector, you can see how CodeView implements this.

This approach uses private assemblies. The code for getting the symbols would look something like this:

var compilerHost = new IDECompilerHost();

var typeEnumerator = (from compiler in compilerHost.Compilers.Cast<IDECompiler>()
                                          from type in compiler.GetCompilation().MainAssembly.Types
                                          select new Tuple<IDECompiler, CSharpType>(compiler, type));

foreach (var typeTuple in typeEnumerator)
{
    Trace.WriteLine(typeTuple.Item2.Name);
    var csType = typeTuple.Item2;
    foreach (var loc in csType.SourceLocations)
    {
       var file = loc.FileName.Value;
       var line = loc.Position.Line;
       var charPos = loc.Position.Character;
    }
}

FileCodeModel + CodeDOM

You could try using the EnvDTE service to get the FileCodeModel associated with a Code Document. This will let you get classes and methods. But it does not support getting the method body. You're messing with buggy COM. This ugly because an COM object reference to a CodeFunction or CodeClass can get invalided without you knowing it, meaning you'd have to keep your own mirror.

Rosalyn AST

This allows provides the same capabilities as both FileCodeModel and Symbols. I've been playing with this and it's actually not too bad.

Unexplored Method

You could try getting the underlying LanguageServiceProvider that is associated with the Code Document. But this is really difficult to pull off, and leaves you with many issues.

查看更多
登录 后发表回答