Text Documents

As previously mentioned, documents can have textual or nontextual content. For those documents with textual content, a separate object exists: TextDocument. The TextDocument object provides access to control functions specifically related to text content.

If you have a valid Document object to start with, and if that Document object refers to a text document, then a TextDocument instance can be referenced from the Document.Object property like this:

TextDocument doc;
Document myDocument;

doc = myDocument.Object;

Table 14.12 contains the TextDocument members.

Image

TABLE 14.12 TextDocument Members


Tip

A text document is represented by both a Document instance and a TextDocument instance. Nontext documents, such as a Windows form, open in a Windows Forms Designer window and have a Document representation but no corresponding TextDocument representation. Unfortunately, there isn’t a great way to distinguish whether a document is text based during runtime. One approach is to attempt a cast or assignment to a TextDocument object and catch any exceptions that might occur during the assignment.


Two TextDocument methods are useful for manipulating bookmarks within the document: ClearBookmarks removes any unnamed bookmarks from the document, and MarkText performs a string pattern search and places bookmarks against the resulting document lines. A simple package to bookmark For loops in a Visual Basic document is presented in Listing 14.6.

LISTING 14.6 Bookmarking For Loops in a Visual Basic Document


using System;
using Extensibility;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualBasic;
using System.Windows.Forms;
using Microsoft.VisualStudio.CommandBars;

public class Connect : IDTExtensibility2
{

     private DTE2 _applicationObject;
     private AddIn _addInInstance;

     public void BookmarkFor()
     {
         Document doc;
         TextDocument txtDoc;

         //Reference the current document
         doc = _applicationObject.ActiveDocument;

         //Retrieve a TextDocument instance from
         //the document
         txtDoc = (TextDocument)doc.Object();

         //Call the MarkText method with the 'For' string
         bool found =
            txtDoc.MarkText("For", (int)vsFindOptions.vsFindOptionsFromStart);

         //MarkText returns a Boolean flag indicating whether or not
         //the search pattern was found in the TextDocument
         if (found)
         {
             MessageBox.Show("All instances of 'For' have been bookmarked.");
         }
         else
         {
             MessageBox.Show("No instances of 'For' were found.");
         }

     }

}


The other key functionality exposed by the TextDocument object is the capability to read and edit the text within the document.

Editing Text Documents

From a Visual Studio perspective, text in a text document actually has two distinct “representations”: a virtual one and a physical one. The physical representation is the straight and unadulterated code file that sits on disk. The virtual representation is what Visual Studio presents on the screen; it is an interpreted view of the text in the code file that takes into account various editor document features such as code outlining/regions, virtual spacing, and word wrapping.

Figure 14.6 shows this relationship. When displaying a text document, Visual Studio reads the source file into a text buffer, and then the text editor presents one view of that text file to you (based on options you have configured for the editor).

Image

FIGURE 14.6 Presentation of text documents within the IDE.

Text in a document is manipulated or read either on the buffered text or on the “view” text that you see in the editor. Four different automation objects enable you to affect text; two work on the text buffer, and two work on the editor view.

For the text buffer:

Image TextPoint objects are used to locate specific points within a text document. By querying the TextPoint properties, you can determine the line number of the text point, the number of characters it is offset from the start of a line, the number of characters it is offset from the start of the document, and its display column within the text editor window. You can also retrieve a reference to a CodeModel object representing the code at the text point’s current location.

Image The EditPoint object inherits from the TextPoint object; this is the primary object used for manipulating text in the text buffer. You can add, delete, or move text using edit points, and you can move the edit points around within the text buffer.

And, for the editor view:

Image The VirtualPoint object is equivalent to the TextPoint object except that it can be used to query text locations that reside in the “virtual” space of the text view. (Virtual space is the whitespace that exists after the last character in a document line.) VirtualPoint instances are returned through the TextSelection object.

Image The TextSelection object operates on text within the text editor view as opposed to the text buffer and is equivalent to the EditPoint interface. When you use the TextSelection object, you are actively affecting the text that is being displayed within the text editor. The methods and properties of this object, therefore, end up being programmatic approximations of the various ways that you would manually affect text: you can page up or page down within the view; cut, copy, and paste text; select a range of text; or even outline and expand or collapse regions of text.

Because the VirtualPoint object is nearly identical to the TextPoint object, and the TextSelection object is nearly identical to the EditPoint object, we won’t bother to cover each of these four objects in detail. Instead, we focus on text buffer operations using EditPoint and TextPoint. You should be able to easily apply the concepts here to the text view.

Because EditPoint objects expose the most functionality and play the central role with text editing, we have provided a list of their type members in Table 14.13.

Image
Image
Image
Image

TABLE 14.13 EditPoint2 Members

Now let’s look at various text manipulation scenarios.

Adding Text

EditPoint objects are the key to adding text. You create them by using either a TextDocument object or a TextPoint object.

A TextPoint instance can create an EditPoint instance in its same location by calling TextPoint.CreateInstance. With the TextDocument type, you can call the CreateEditPoint method and pass in a valid TextPoint.

Because TextPoint objects are used to locate specific points in a document, a TextPoint object is leveraged as an input parameter to CreateEditPoint. In essence, the object tells the method where to create the edit point. If you don’t provide a TextPoint object, the edit point is created at the start of the document.

This code snippet shows an edit point being created at the end of a document.

Document doc = _applicationObject.ActiveDocument;
TextDocument txtDoc = (Textdocument)doc.Object();
TextPoint tp = txtDoc.EndPoint;
EditPoint2 ep = txtDoc.CreateEditPoint(tp);
//This line of code would have the same effect
ep = tp.CreateEditPoint();

After creating an edit point, you can use it to add text into the document. (Remember, you are editing the buffered text whenever you use an EditPoint object.) To inject a string into the document, you use the Insert method:

//Insert a C# comment line
ep.Insert("// some comment");

You can even grab the contents of a file and throw that into the document with the EditPoint.InsertFromFile method:

//Insert comments from a comments file
ep.InsertFromFile("C:Contosostd comments.txt");

Editing Text

The EditPoint object supports deleting, replacing, cutting, copying, and pasting text in a document.

Some of these operations require more than a single point to operate. For instance, if you want to cut a word or an entire line of code from a document, you need to specify a start point and end point that define that range of text (see Figure 14.7).

Image

FIGURE 14.7 Using points within a document to select text.

This snippet uses two end points—one at the start of a document and one at the end—to delete the entire contents of the document.

Document doc = _applicationObject.ActiveDocument;
TextDocument txtDoc = (TextDocument)doc.Object();

TextPoint tpStart = txtDoc.StartPoint;
TextPoint tpEnd = txtDoc.EndPoint;

EditPoint2 epStart = txtDoc.CreateEditPoint(tpStart);
EditPoint2 epEnd = txtDoc.CreateEditPoint(tpEnd);
epStart.Delete(epEnd);

Besides accepting a second EditPoint, the methods that operate on a range of text also accept an integer identifying a count of characters. This has the effect of defining a select. For example, this snippet cuts the first 10 characters from a document.

epStart.Cut(10);

Repositioning an EditPoint

After establishing an EditPoint, you can move it to any location in the document by using various methods. The CharLeft and CharRight methods move the point any number of characters to the left or right, and the WordLeft and WordRight methods perform the same operation with words.

// Move the edit point four words to the right
epStart.WordRight(4);

The LineUp and LineDown methods jog the point up or down the specified number of lines. You can also move EditPoints to any given line within a document by using MoveToLineAndOffset. In addition, this method positions the point any number of characters into the line.

// Move the edit point to line 100, and then
// in 5 characters to the right
epStart.MoveToLineAndOffset(100, 5);

To illustrate some of these text editing concepts, consider the task of programmatically adding a comment “flower box” immediately preceding a routine open in a code editor. To accomplish this, we would need to go through the following process:

1. Obtain a reference for the current document in the IDE.

2. Get the active cursor location in that document via the TextDocument.Selection.ActivePoint property.

3. Create an EditPoint using the VirtualPoint object.

4. Create a second EditPoint to act as the other “book end” for the text. In other words, these two edit points will represent the start and the end of the routine definition line (ex: public void DoSomething(int someArg)).

5. Parse the routine definition text (encapsulated by the endpoints) to try to ferret out items such as its name, return value, and parameter list.

6. Build a string using the routine information and then insert that string into the code editor/text document using an EditPoint.

Listing 14.7 demonstrates the preceding actions.

LISTING 14.7 Inserting Comments into a Text Window


using System;
using Extensibility;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualBasic;
using System.Windows.Forms;
using Microsoft.VisualStudio.CommandBars;
public class Connect : IDTExtensibility2
{

     private DTE2 _applicationObject;
     private AddIn _addInInstance;

     //This routine demonstrates various text editing scenarios
     //using the EditPoint and TextPoint types. If you place your
     //cursor on a Visual Basic subroutine or function, it will build
     //a default "flower box" comment area, insert it immediately
     //above the sub/function, and outline it.
     //
     //To use:
     //  1) put cursor anywhere on the Sub/Function line
     //  2) run add-in command
     //  This will fail silently (e.g., will not insert any
     //  comments) if it is unable to determine the start
     //  of the Sub/Function
     //
     public void  InsertVBTemplateFlowerbox()
     {
         //Get reference to the active document
         Document doc = _applicationObject.ActiveDocument;
         TextDocument txtDoc = (TextDocument)doc.Object();
         bool isFunc;

         try
         {
             EditPoint2 ep = (EditPoint2)txtDoc.Selection.ActivePoint.CreateEditPoint();

             ep.StartOfLine();
             EditPoint2 ep2 = (EditPoint2)ep.CreateEditPoint();
             ep2.EndOfLine();

             string lineText = ep.GetText(ep2).Trim();

             if (lineText.IndexOf(" Function ") > 0)
             {
                 isFunc = true;
             }
             else
             {
                 if (lineText.IndexOf(" Sub ") > 0)
                 {
                     isFunc = false;
                 }

                 else
                 {
                     throw new Exception();
                 }
             }

             //Parse out info that we can derive from the routine
             //definition: the return value type (if this is a function),
             //the names of the parameters, and the name of the routine.
             string returnType = "";

             if (isFunc)
             {
                 returnType = ParseRetValueType(lineText);
             }


             string[] parameters = ParseParameters(lineText);
             string name = ParseRoutineName(lineText);
             string commentBlock = BuildCommentBlock(isFunc, name,
                returnType, parameters);

             //Move the edit point up one line (to position
             //immediately preceding the routine)
             ep.LineUp(1);

             //Give us some room by inserting a new blank line
             ep.InsertNewLine();

             //Insert our comment block
             ep.Insert(commentBlock.ToString());
         }
         catch (Exception ex)
         {

         }


     }

     private string BuildCommentBlock(bool isFunc,
         string name,
         string returnType,
            string[] parameters)

     {
         try
         {
             string comment = "";

             //Build up a sample comment block using the passed-in info
             comment += "/////////////////////////////////////////////// ";
             comment += "// Routine: " + name;
             comment += " ";
             comment += "// Description: [insert routine desc here]";
             comment += " ";
             comment += "//";
             comment += " ";

             if (isFunc)
             {
                 comment += "// Returns: A " +
                     returnType +
                     "[insert return value description here]";
             }

             comment += " ";
             comment += "//";
             comment += " ";
             comment += "// Parameters:";
             comment += " ";

             for (int i = 0; i <= parameters.GetUpperBound(0); i++)
             {
                 comment += "//     ";
                 comment += parameters[i];
                 comment += ": [insert parameter description here]";
                 comment += " ";
             }

             comment += "/////////////////////////////////////////////// ";

             return comment;

         }
         catch (Exception ex)
         {
             return "";
         }


     }

     private string ParseRetValueType(string code)
     {
         try
         {
             //Parse out the return value of a function (VB)
             //Search for //As', starting from the end of the string
             int length = code.Length;
             int index = code.LastIndexOf(" As ");

             string retVal = code.Substring(index + 3, length - (index + 3));
             return retVal.Trim();

         }

         catch (Exception ex)
         {
             return "";
         }


     }

     private string[] ParseParameters(string code)
     {
         try{
             //Parse out the parameters specified (if any) for
             //a VB sub/func definition
             int length = code.Length;
             int indexStart = code.IndexOf("(");
             int indexEnd = code.LastIndexOf(")");

             string parameters = code.Substring(indexStart + 1, indexEnd -
              (indexStart + 1));

             return parameters.Split(','),

         }
         catch (Exception ex)
         {
             return null;
         }

     }

     private string ParseRoutineName(string code)

     {
         try
         {
             string name;
             int length = code.Length;
             int indexStart = code.IndexOf(" Sub ");
             int indexEnd = code.IndexOf("(");

             if (indexStart == -1)
             {
                 indexStart = code.IndexOf(" Function ");
                 if (indexStart != -1)
                 {
                     indexStart = indexStart + 9;
                 }

             }
             else
             {
                 indexStart = indexStart + 5;
             }

             name = code.Substring(indexStart, indexEnd - indexStart);

             return name.Trim();
         }
         catch (Exception ex)
         {
             return "";
         }

     }

}


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset