For each method in file
ForEachMethodInFile is a Visual Studio macro that lets you do custom actions for each method defined in the current file. I’ve used it in the past to generate logging code to log the start and end of each method, to generate default error handling code etc.. And today someone over at CodeProject wanted to generate a breakpoint at the start of each method. The common thread here is the performing of some custom action for each method in the current file. I figured it would be useful for a lot of people if the “for each method in file” logic is available as a separate library, and that is what is ForEachMethodInFile.
The macro project is available here. To provide a custom action, all you have to do is create a new macro project, and do
1: Imports System
2: Imports EnvDTE
3: Imports EnvDTE80
4: Imports EnvDTE90
5: Imports System.Diagnostics
6: Imports ForEachMethodInFile
7:
8: Public Module BreakOnEachMethodEntry
9:
10: Public Sub Run()
11: ForEachMethod.DoAction(AddressOf AddBreakPoint)
12: End Sub
13:
14: Function AddBreakPoint(ByVal method As CodeFunction)
15: DTE.Debugger.Breakpoints.Add(method.Name)
16: End Function
17:
18: End Module
You define a callback function that takes the COM object representing a method as a parameter, and then pass on the function to the DoAction method. Your function will be called for each method defined in the file, and the cursor will be positioned at the start of the method before the callback occurs. In this case, I want to add a breakpoint, so I call the appropriate DTE method, passing the current method’s name to tell it where to place the breakpoint.
Here’s the entire code in the ForEachMethodInFile project – it basically recursively traverses code elements in the file, and invokes the callback when it runs into a method.
1: Imports System
2: Imports EnvDTE
3: Imports EnvDTE80
4: Imports System.Diagnostics
5:
6: Public Module ForEachMethod
7:
8: Public Sub DoAction(ByRef action As Action(Of CodeFunction))
9: ProcessFile(action)
10: End Sub
11:
12: Function ProcessFile(ByRef action As Action(Of CodeFunction))
13: Dim selection As EnvDTE.TextSelection
14: Dim projectItem As ProjectItem
15: Dim fileCodeModel As FileCodeModel
16: Dim codeElement As CodeElement
17: Dim i As Integer
18:
19: Dim currentFunction As CodeFunction
20:
21: projectItem = DTE.ActiveDocument.ProjectItem
22:
23: fileCodeModel = projectItem.FileCodeModel
24: For i = 1 To fileCodeModel.CodeElements.Count
25: codeElement = fileCodeModel.CodeElements.Item(i)
26: ProcessCodeElement(codeElement, action)
27: Next
28:
29: ' Reformat the modified code
30: selection = DTE.ActiveDocument.Selection
31: selection.SelectAll()
32: selection.SmartFormat()
33: End Function
34:
35: Sub ProcessNamespace(ByVal namespaceElement As CodeNamespace, ByRef action As Action(Of CodeFunction))
36: Dim i As Integer
37: Dim codeElement As CodeElement
38:
39: For i = 1 To namespaceElement.Members.Count
40: codeElement = namespaceElement.Members.Item(i)
41: ProcessCodeElement(codeElement, action)
42: Next
43: End Sub
44:
45: Sub ProcessCodeElement(ByVal codeElement As CodeElement, ByRef action As Action(Of CodeFunction))
46: If codeElement.Kind = vsCMElement.vsCMElementNamespace Then
47: ProcessNamespace(codeElement, action)
48: ElseIf codeElement.Kind = vsCMElement.vsCMElementClass Then
49: ProcessType(codeElement, action)
50: ElseIf codeElement.Kind = vsCMElement.vsCMElementFunction Then
51: ProcessMethod(codeElement, action)
52: End If
53: End Sub
54:
55: Sub ProcessType(ByVal typeElement As CodeClass, ByRef action As Action(Of CodeFunction))
56: Dim i As Integer
57: Dim codeElement As CodeElement
58:
59: For i = 1 To typeElement.Members.Count
60: codeElement = typeElement.Members.Item(i)
61: If codeElement.Kind = vsCMElement.vsCMElementFunction Then
62: ProcessMethod(codeElement, action)
63: ElseIf codeElement.Kind = vsCMElement.vsCMElementClass Then
64: ProcessType(codeElement, action)
65: End If
66: Next
67: End Sub
68:
69: Sub ProcessMethod(ByVal methodElement As CodeFunction, ByRef action As Action(Of CodeFunction))
70: Dim selection As EnvDTE.TextSelection
71: Dim editPoint As EnvDTE.EditPoint
72: Dim verifyPoint As EnvDTE.TextPoint
73: Dim endPointAbsCharOffset As Integer
74: Dim column As Integer
75: Dim methodRunNotifierSignature As String
76: Dim functionStartCode As String
77: Dim functionEndCode As String
78: Dim parameters As String
79: Dim parameter As EnvDTE80.CodeParameter2
80: Dim i As Integer
81:
82: If methodElement.MustImplement Then
83: Return
84: End If
85:
86: selection = DTE.ActiveDocument.Selection
87: editPoint = selection.ActivePoint.CreateEditPoint()
88: verifyPoint = selection.ActivePoint.CreateEditPoint()
89:
90: ' Move to start of method
91: editPoint.MoveToPoint(methodElement.GetStartPoint(vsCMPart.vsCMPartBody))
92: selection.MoveToPoint(editPoint)
93: verifyPoint.MoveToPoint(methodElement.GetStartPoint(vsCMPart.vsCMPartBody))
94:
95: action(methodElement)
96:
97: End Sub
98: End Module
99:
100: