Code Model & its Interop with Text Editor Object Model
The Visual Studio Code Model can be used to read and edit the structure of the code in a file. The model gives you the ability to interact with files from different langauges in a common way without parsing the code. With this model, an automation client (e.g. add-in/macro) would start from a solution or project to get to a project item, and from the project item to get to the code model. Specifically, from a ProjectItem, you get to a FileCodeModel object that gets you to a CodeElements collection. At the FileCodeModel level, it contains a CodeElement object for each top-level code element in the file (e.g. Implements statement, WithEvents declaration, etc). At the class level, the CodeElements collection contains a CodeElement for each member of the class (e.g. a code function). You can QI (or cast in C# and VB) to get to more powerful language-specific interfaces.
A key functionality of code model is the ability to interop with the Text Editor OM. In other words, an automation client can go from the editor's automation model to a code model object and from a code model object to the start and end point of the code as text in the editor (for example, you have a Method object in hand, get its StartPoint, and inject a few lines of code, or you handle the LineChanged event, get the EditPoint in the event, and map it to a Method code object).
Following are some macro samples that demonstrate how to walk through the code model hierarchy of a solution and how to inteop with Text Editor Object Model.
Sub walkThroughFileCodeModel()
Dim projectItems As ProjectItems
Dim projectItem As ProjectItem
Dim fileCodeModel As FileCodeModel
Dim codeElements As CodeElements
Dim sRes As String = ""
Dim level As Integer
For Each proj As Project In DTE.Solution
projectItems = proj.ProjectItems
For Each projectItem In projectItems
'Debug.WriteLine(projectItem.Name)
fileCodeModel = projectItem.FileCodeModel
level = -1
If Not fileCodeModel Is Nothing Then
codeElements = fileCodeModel.CodeElements
codeElementChildren(codeElements, sRes, level)
End If
sRes += vbNewLine
Next
Next
MsgBox(sRes)
End Sub
Sub codeElementChildren(ByVal codeElements As CodeElements, ByRef sRes As String, ByVal level As Integer)
Dim i As Integer
level += 1
For Each clt As CodeElement In codeElements
For i = 1 To level
sRes += vbTab
Next
Try
sRes += String.Format("{0}, Kind = {1} Line:{2} ~ {3} ", clt.Name, clt.Kind, clt.StartPoint.Line, clt.EndPoint.Line)
Catch ex As System.Exception
If TypeOf clt Is EnvDTE80.CodeImport Then
Dim ci As EnvDTE80.CodeImport = CType(clt, EnvDTE80.CodeImport)
sRes += String.Format("{0}, Kind = {1} Line:{2} ~ {3} ", ci.Namespace, clt.Kind, clt.StartPoint.Line, clt.EndPoint.Line)
Else
sRes += String.Format(" , Kind = {0} Line:{1} ~ {2} ", clt.Kind, clt.StartPoint.Line, clt.EndPoint.Line)
End If
End Try
sRes += vbNewLine
Try
If Not clt.Children Is Nothing Then
codeElementChildren(clt.Children, sRes, level)
End If
Catch ex As System.Exception
Debug.WriteLine(ex.Message)
End Try
Next
End Sub
Sub fromCodelModel2TextEditor()
Dim fcm As FileCodeModel = DTE.ActiveDocument.ProjectItem.FileCodeModel
Dim cns As CodeNamespace = fcm.CodeElements.Item("ClassLibraryCS")
Dim cc As CodeClass = cns.Children.Item("Class1")
Dim tp As TextPoint = cc.GetEndPoint 'cc.GetStartPoint
Dim ep As EditPoint = tp.CreateEditPoint
ep.Insert("blah")
End Sub
Sub fromTextEditor2CodeModel()
Dim td As TextDocument = DTE.ActiveDocument.Object
Dim cc As CodeClass = td.Selection.ActivePoint.CodeElement(vsCMElement.vsCMElementClass)
Debug.WriteLine(cc.Name)
End Sub