8

I want to programmatically add a function (a TestMethod) to an existing C#-file. After some googling I have found the library EnvDTE and CodeModel.AddFunction-Method, but I can't find a good example of what I want.

I would like to add a function with code already in that newly created function and also with an attribute. Something like this:

/// <summary>
/// Documentation
/// </summary>
[TestMethod]
public void TestMethod1()
{
    string test = Helper.CodeExample();
}

Can anyone show me an example on how to do this?

EDIT: I want to edit a C# file, like you would edit a text-file. I know you could do this with a StreamWriter, but is there maybe a better way for doing this?

Mathieu
  • 3,073
  • 1
  • 19
  • 25
  • Do you mean modifying a C# (text) file or C# (.NET) class? Modifying text is totally different from modifying compiled code. – larsmoa Nov 14 '12 at 11:02
  • I mean a C# text-file. It's for generating test-files. I can do this with a StreamWriter, but i though there maybe is a better way for doing this. – Mathieu Nov 14 '12 at 12:14
  • You need to find something that can parse the code into a syntax tree, then adapt that syntax tree to add your function and then generate a new file from that syntax tree. – Tamara Wijsman Nov 14 '12 at 12:37
  • Visual Studio can do that for him (using EnvDTE). No need to reinvent the wheel :-) – Francesco Baruchelli Nov 15 '12 at 09:29

1 Answers1

7

EnvDTE can be the way to go. You can develop a VisualStudio Add-In and then modify the Exec method. In this method you have to get the active document and its ProjectItem. This is where you find the CodeModel which contains a lot of CodeElements. Among these elements you have to find the CodeNamespace, and inside this element the CodeClass. This is the object that responds to AddFunction returning the new CodeFunction to which you can add your attribute and the code (this is a part I don't like too much, since you have to use EditPoint).

This is a very simple version of Exec that you can use as a starting point:

    public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
    {
        handled = false;
        if(executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
        {
            handled = true;
            if (commandName == "TestAddMethod.Connect.TestAddMethod")
            {
                Document activeDoc = _applicationObject.ActiveDocument;
                if (activeDoc == null)
                    return;
                ProjectItem prjItem = activeDoc.ProjectItem;
                if (prjItem == null)
                    return;
                FileCodeModel fcm = prjItem.FileCodeModel;
                if (fcm == null)
                    return;

                CodeElements ces = fcm.CodeElements;
                // look for the namespace in the active document
                CodeNamespace cns = null;
                foreach (CodeElement ce in ces)
                {
                    if (ce.Kind == vsCMElement.vsCMElementNamespace)
                    {
                        cns = ce as CodeNamespace;
                        break;
                    }
                }
                if (cns == null)
                    return;
                ces = cns.Members;
                if (ces == null)
                    return;
                // look for the first class
                CodeClass cls = null;
                foreach (CodeElement ce in ces)
                {
                    if (ce.Kind == vsCMElement.vsCMElementClass)
                    {
                        cls = ce as CodeClass;
                        break;
                    }
                }
                if (cls == null)
                    return;
                CodeFunction cf = cls.AddFunction("TestMethod1", vsCMFunction.vsCMFunctionFunction, vsCMTypeRef.vsCMTypeRefVoid, -1, vsCMAccess.vsCMAccessPrivate);
                cf.AddAttribute("TestMethod", "true");
                TextPoint tp = cf.GetStartPoint(vsCMPart.vsCMPartBody);
                EditPoint ep = tp.CreateEditPoint();
                ep.Indent();
                ep.Indent();
                ep.Indent();
                ep.Insert("string test = Helper.CodeExample();");
            }
        }
    }
Francesco Baruchelli
  • 7,320
  • 2
  • 32
  • 40
  • This is what i'm looking for! This is way better than just editing the cs-file like a text-file. Thanks a lot! – Mathieu Nov 15 '12 at 10:53
  • 1
    This was a fantastic starting point for me! Thank you! Also worth noting is being able to do `SmartFormat` rather than the indents. It smart formats a range of code starting at the calling `EditPoint` and ending at the parameter `TextPoint`. Here's an example using the above code: `ep.Insert("string test = Helper.CodeExample();");` `tp.CreateEditPoint().SmartFormat(ep);` – philt5252 Jan 12 '13 at 05:42