| < Free Open Study > |
|
Now you're going to use the new function that I just created in the Smart Desktop add-in that you've been developing thus far in the book. First, use the new procedure in the CloneProcedure feature, so that the developer will not have to select the whole procedure before invoking the add-in. This will make it easier and faster to use and, after all, the primary reason for writing add-ins is to increase the developer's productivity. You can find the code for these new features in MyAddinTest1 in the code for Chapter 8. If you're following along with me, load the project into Visual Studio .NET.
To use this new procedure, perform the following tasks:
Add the new GetWholeProc method to the Connect class.
Change the call from GetCodeFromWindow to GetWholeProc.
Introduce some new features to the add-in that will use the GetWholeProc method.
Listing 8-8 shows the CloneProcedure method of the Connect class. The only line changed is in bold. The call to GetCodeFromWindow changed to a call to GetWholeProc.
Listing 8-8: The Modified CloneProcedure Method
Shared Sub CloneProcedure() Dim s As String Dim i As Integer Dim rs As String Try ' get selected proc from active window s = GetWholeProc() ' GetCodeFromWindow() If InStr(1, s, " Sub ", 0) = 0 And _ InStr(1, s, " Function ", CompareMethod.Binary) = 0 Then MsgBox("Please select a whole Procedure to be cloned.", MsgBoxStyle.Exclamation) Exit Sub End If Dim oFrm As New CloneProc() rs = oFrm.Display(s) oFrm.Dispose() If rs <> "" Then AddMethodToEndOfDocument(rs) End If Catch e As System.Exception 'MsgBox("Error: " & e.Message, MsgBoxStyle.Critical, "Clone Procedure") Exit Sub End Try End Sub
Next, you add the new method, GetWholeProc, to the Connect class. Copy it from the macro module into the Connect class and place it at the end of the class. You make two changes to the method when you move it. First, you add the keyword Shared to the function definition line. This will allow it to be seen from anywhere in the project. Second, you substitute the object oVB for DTE in the one line where it is used. Listing 8-9 displays the new method with the changed lines in bold.
Listing 8-9: The GetWholeProc Method Modified for Use in the Add-in
Shared Function GetWholeProc() As String Dim ts As TextSelection = oVB.ActiveWindow().Selection Dim ep As EditPoint = ts.ActivePoint.CreateEditPoint Dim sLine As String Dim i As Integer Try ' if the user has selected the whole proc, ' then just return it ' otherwise select it for them... If Len(ts.Text) > 0 Then If (InStr(1, ts.Text, "Sub ", 1) > 0 Or _ InStr(1, ts.Text, "Function ", 1) > 0) And _ (InStr(1, ts.Text, "End Sub", 1) > 0 Or _ InStr(1, ts.Text, "End Function", 1) > 0) _ Then Return ts.Text End If GoTo SelectTheProc Else SelectTheProc: " Get the start of the proc ep.MoveToPoint(ep.CodeElement(EnvDTE.vsCMElement. vsCMElementFunction).GetStartPoint(vsCMPart.vsCMPartWhole)) ' move selection start point to top of proc ts.MoveToPoint(ep, False) ' back up to previous line looking for comments i = 0 Do ep.LineUp() ts.MoveToPoint(ep, False) ts.SelectLine() sLine = ts.Text If Left(Trim(sLine), 1) <> ""' Then ep.LineDown() ts.MoveToPoint(ep, False) Exit Do End If i = i + 1 Loop ' if the count of comment lines > 0 the ts point is set properly ' else we must move it back to the original ep.LineDown(i + 1) ' move to bottom of proc ep.MoveToPoint(ep.CodeElement(EnvDTE.vsCMElement .vsCMElementFunction).GetEndPoint(vsCMPart.vsCMPartWhole)) ' select the proc ts.MoveToPoint(ep, True) Return ts.Text End If Catch System.Windows.Forms.MessageBox.Show ("You must either select the whole procedure or your cursor must be within the procedure to be selected.") Return "" End Try End Function
Now that you've made all of the changes to use the new and improved method for retrieving a procedure, run the add-in to test the changes. Because you already have the add-in (MyAddinTest1 from the Chapter 8 code) loaded into Visual Studio, simply press F5 to test the add-in. Assuming that you've made the changes correctly and have no compile errors, the second copy (add-in client) of Visual Studio .NET should start automatically. When it does, select Chap 8 Sample Project1 from the code for Chapter 8 as the project to run. When the sample project is loaded into the client copy of Visual Studio, open the Add-in Manager dialog box, check both boxes for MyFirstAddin1, and click OK to close the dialog box. This will connect the add-in.
From the Tools menu, select the Smart Desktop menu option. It should be the top menu item on the Tools menu. This will cause the add-in's UI form to appear. Open the TreeView. If Form1.vb (code window) is not already open in the client copy of Visual Studio .NET, open it and place the cursor anywhere in the TestMacroProcedureSelecting method. Next, click the CloneProcedure node in the TreeView. The code for the selected procedure will be retrieved and displayed in the Clone Procedure form, as shown in Figure 8-11.
Figure 8-11: Clone procedure display form
At this point, you're going to change the name of the procedure in the CloneProc form to NewTestProc. Finally, click the PasteToModule button on the form. This causes the form to paste the cloned procedure back to the client code window. The cloned procedure is shown in Figure 8-12.
Figure 8-12: New cloned procedure
To close the test, disconnect the add-in by deselecting the add-in in the Add-in Manager dialog box. Next, return to the copy of Visual Studio where the add-in is running and click the Stop Debugging button. This will automatically close the client instance of Visual Studio.
In this section, I introduce one additional feature for the Smart Desktop add-in that will use the GetWholeProc method. This feature can retrieve the comments that are immediately before and following a procedure definition line. It takes those comments, extracts the parameters, and applies a document template to the extracted data. It formalizes the programmer-supplied comments, adds some additional data, and creates a formal-looking documentation template. It then puts the procedure back into the module.
Obviously, many of the data parameters and the template itself are hardcoded in this add-in. In a real add-in, the template would be read from a file, which could be customized. The user name and company name would be retrieved from the registry.
The code in Listing 8-10 does the work of reading through the captured procedure and applying the formal document template to the procedure. I don't explain this code in detail, mainly due to its length and because it's very straight-forward in what it does. At a high level, it does the following things:
Extracts comment lines before and following the procedure definition line
Extracts the parameters from the procedure definition line
Extracts the return value if the procedure is a function
Substitutes live values for keywords found in the template, such as Copyright, Created By, Created Date, and so forth
Concatenates all the template data and places it into the procedure
Returns the procedure to the module
Listing 8-10: DocTemplate Method
Shared Sub DocTemplate() ' Apply a formal template to the captured procedure Dim i As Long Dim iPtr As Integer Dim nL As Integer Dim bParameters As Boolean Dim bPurpose As Boolean Dim sOut As String Dim sLine As String Dim bFoundSub As Boolean Dim sDocTemplate As String Dim sWord As String Dim sTempParams As String Dim cComStr As String Dim sPar As String Dim sRetVal As String Dim sProcToCopy As String Dim sProcType As String Dim j As Integer Dim k As Integer Const sPurpose = "Purpose:" Const sParameters = "Parameters:" Const sDateCreated = "Date Created:" Const sAuthor = "Author:" Const sUserName = "Les Smith" Const sCopyright = "Copyright:" Const sCompanyName = "HHI Software, Inc." Const sReturns = "Returns:" Dim s As String Try ' create the doc template ' In a production add-in this would be read from a file ' so that the file could be customized... s = "'***************************************" & vbCrLf s = s & "'* Name: ProcName" & vbCrLf s = s & "'* Purpose:" & vbCrLf s = s & "'*" & vbCrLf s = s & "'* " & vbCrLf s = s & "'* Parameters:" & vbCrLf s = s & "'*" & vbCrLf s = s & "'* Returns:" & vbCrLf s = s & "'*" & vbCrLf s = s & "'* Author: " & vbCrLf s = s & "'* Date Created:" & vbCrLf s = s & "'* CopyRight: " & vbCrLf s = s & "'* Date Last Changed: " & vbCrLf s = s & "'***************************************" & vbCrLf sDocTemplate = s ' if user selected the text prior to clicking the ' button, get that code sProcToCopy = "" sProcToCopy = GetWholeProc() sOut = "" sDocTemplate = "" bFoundSub = False ' find out some stuff about the template bPurpose = (InStr(1, sDocTemplate, sPurpose, 1) > 1) bParameters = (InStr(1, sDocTemplate, sParameters, 1) > 1) nL = MLCount(sProcToCopy, 0) cComStr = "" For i = 1 To nL sLine = MemoLine(sProcToCopy, 0, i) If Not bFoundSub Then If InStr(sLine, "Sub ") > 0 Or _ InStr(sLine, "Function ") > 0 Then sOut = sOut & sLine & vbCrLf bFoundSub = True ' get sub name Do While Trim$(sLine) <> "" sWord = GetToken(sLine, "") If InStr("Sub_Function", sWord) > 0 Then Exit Do sProcType = sWord Loop ' get sub name sWord = GetToken(sLine, "_") 'get parameters if applicable If bParameters Then sRetVal = "" sTempParams = "" j = CountOccurrences(",", sLine) For k = 1 To j ' the next parm sPar = Left$(sLine, InStr(sLine, ",") - 1) ' remove it from sline sLine = Mid$(sLine, InStr(sLine, ",") + 1) sTempParams = sTempParams & "'* " & _ Trim(sPar) & vbCrLf Next k ' get last parm k = InStr(sLine, ")") - 1 If k > 0 Then sPar = Left$(sLine, InStr(sLine, ")") - 1) sTempParams = sTempParams & "'* " & _ Trim(sPar) & vbCrLf End If sLine = Mid$(sLine, InStr(sLine, ")")) sPar = Trim$(GetToken(sLine, " _")) If Left$(sPar, 3) = "As " Then sRetVal = Mid$(sPar, 4) End If End If ' now get any comments after the proc def line Do While i <= nL i = i + 1 sLine = MemoLine(sProcToCopy, 0, i) ' if this is a comment line let's uncomment it and ' put in cComStr, if a blank, skip it... If Left$(Trim$(sLine), 2) = "'*" Then If InStr(sLine, "***") > 1 Or _ InStr(sLine, "--") > 1 Or _ InStr(sLine, "$$$") > 1 Or _ InStr(sLine, "___") > 1 Then Else sLine = Trim$(sLine) sLine = Mid$(sLine, 3) sLine = Trim$(sLine) cComStr = cComStr & "'* " & sLine & vbCrLf End If ElseIf Left$(Trim$(sLine), 1) = ""' Then If InStr(sLine, "***") > 1 Or _ InStr(sLine, "--") > 1 Or _ InStr(sLine, "$$$") > 1 Or _ InStr(sLine, "___") > 1 Then Else sLine = Trim$(sLine) sLine = Mid$(sLine, 2) sLine = Trim$(sLine) cComStr = cComStr & "'* " & sLine & vbCrLf End If ElseIf Trim$(sLine) = "" Then ' discard blank lines before Sub/Function Else Exit Do End If Loop If Trim$(cComStr) <> "" Then ' strip the last crlf so we don't get a blank line iPtr = InStrRev(cComStr, vbCrLf) If iPtr > 0 Then cComStr = Left$(cComStr, iPtr - 1) End If End If If Trim$(sTempParams) <> "" Then ' strip the last crlf so we don't get a blank line iPtr = InStrRev(sTempParams, vbCrLf) If iPtr > 0 Then sTempParams = Left$(sTempParams, iPtr - 1) End If End If ' substitute into the template sDocTemplate = Replace(sDocTemplate, _ "ProcName", sWord) sDocTemplate = Replace(sDocTemplate, _ sCopyright, _ sCopyright & " " & _ sCompanyName) sDocTemplate = Replace(sDocTemplate, _ sDateCreated, _ sDateCreated & " " & _ TodaysDate) sDocTemplate = Replace(sDocTemplate, _ sAuthor, _ sAuthor & " " & _ sUserName) If bPurpose And Trim$(cComStr) <> "" Then sDocTemplate = Replace(sDocTemplate, _ sPurpose, _ sPurpose & " " & _ vbCrLf & cComStr) End If If bParameters And Trim$(sTempParams) <> "" Then sDocTemplate = Replace(sDocTemplate, _ sParameters, _ sParameters & " " & _ vbCrLf & sTempParams) End If If sRetVal <> "" Then sDocTemplate = Replace(sDocTemplate, _ sReturns, _ sReturns & " " & sRetVal) ElseIf sProcType = "Sub" Then sDocTemplate = Replace(sDocTemplate, sReturns, "") Else sDocTemplate = Replace(sDocTemplate, _ sReturns, _ sReturns & _ " Return Value not specified") End If sOut = sOut & sDocTemplate sOut = sOut & sLine & vbCrLf Else ' we have not found the proc def line yet ' if this is a comment line let's uncomment it and ' put in cComStr, if a blank, skip it... If Left$(Trim$(sLine), 2) = "'*" Then If InStr(sLine, "***") > 1 Or _ InStr(sLine, "--") > 1 Or _ InStr(sLine, "$$$") > 1 Or _ InStr(sLine, "___") > 1 Then Else sLine = Trim$(sLine) sLine = Mid$(sLine, 3) sLine = Trim$(sLine) cComStr = cComStr & "'* " & sLine & vbCrLf End If ElseIf Left$(Trim$(sLine), 1) = ""' Then If InStr(sLine, "***") > 1 Or _ InStr(sLine, "--") > 1 Or _ InStr(sLine, "$$$") > 1 Or _ InStr(sLine, "___") > 1 Then Else sLine = Trim$(sLine) sLine = Mid$(sLine, 2) sLine = Trim$(sLine) cComStr = cComStr & "'* " & sLine & vbCrLf End If ElseIf Trim$(sLine) = "" Then ' discard blank lines before Sub/Function Else sOut = sOut & sLine & vbCrLf End If End If Else sOut = sOut & sLine & vbCrLf End If Next ' paste the code back to the window PutCodeBack(sOut) Exit Sub Catch e As System.Exception End Try End Sub
In addition to adding this code to the Connect class, you should add a new node to the TreeView in Form1 and name the node Document Procedure. You'll see the new node when you execute the add-in. I'm sure that by now you're familiar with how to add a node to the TreeView, so I won't go through that process.
Listing 8-11 shows a helper function called by DocTemplate. It simply counts occurrences of an expression in a string.
Listing 8-11: CountOccurrences Helper Method
Shared Function CountOccurrences(ByVal rsExp As String, _ ByVal rsStr As Object) As Long ' Returns the number of occurrences of rsExp (expression) ' found in rsStr (string) ' Returns 0 of no occurrences found. Dim pPos As Integer Dim lPos As Integer Dim nPos As Integer Dim nFirst As Integer Dim lCnt As Integer Try pPos = 0 ' previous find lPos = 0 ' return position of right char nPos = 1 ' position of next right most char nFirst = 1 lCnt = 0 ' loop thru every char in string until we ' find the last occurrence Do lPos = InStr(nPos, rsStr, rsExp, 1) If lPos > 0 Then nPos = lPos + 1 pPos = lPos lCnt = lCnt + 1 Else Exit Do End If Loop Return lCnt Catch e As System.Exception End Try End Function
Listing 8-12 shows the code added to the TreeView event handler in Form1.vb. The new code is in boldface. It calls the DocTemplate method of the Connect class to retrieve and document the selected procedure.
Listing 8-12: AfterSelect Event of TreeView
Private Sub tvMenu_AfterSelect(ByVal sender As Object, _ ByVal e As System.Windows.Forms.TreeViewEventArgs) _ Handles tvMenu.AfterSelect Dim i As Integer Select Case UCase$(e.Node.Text) Case "SMART DESKTOP" 'ignore root clicks Case "BLOCK COMMENT" Call Connect.BlockComment() Case "UNCOMMENT" Call Connect.BlockUnComment() Case "BLOCK CHANGE" Call Connect.BlockChange() Case "BLOCK DELETE" Call Connect.BlockDelete() Case "PROC ERROR HANDLER" Call Connect.GenLocalErrorTrap() Case "CLONE PROCEDURE" Call Connect.CloneProcedure() Case "DOCUMENT PROCEDURE" Call Connect.DocTemplate() Case Else MsgBox("Please click on a Child Node.", _ MsgBoxStyle.Information, "Unknown Request") End Select End Sub
Now I have shown all of the code that has been added to retrieve and document the selected procedure. To run the add-in, follow the normal procedure of debugging: Press F5. When the second copy of the Visual Studio opens, connect the add-in by going to the Add-in Manager and checking both boxes representing MyAddinTest1.
You should open Chap 8 Sample Project1 and open the Form1.vb code module. Place the cursor anywhere in the TestMacroProcedureSelecting procedure. Display the add-in's TreeView menu form and select the Document Procedure node. Listing 8-13 shows the procedure before it has been documented.
Listing 8-13: Procedure Before Being Documented
' Comment for TestMacroCommenting ' Comment Line 2 Sub TestMacroProcedureSelecting() Dim s As String Dim i As Integer Dim l As Long s = "String" l = 12 i = 231 End Sub
After the code has been documented and placed back into the module, it will appear as shown in Figure 8-13.
Figure 8-13: Documented code
| < Free Open Study > |
|