© 2004 Microsoft Corporation. All rights reserved.

Figure 5 vsInsertFlags Values

vsInsertFlagsCollapseToEnd
The selection's current contents are deleted before performing the insertion, and the TextSelection is left empty at the end of the newly inserted text.
vsInsertFlagsCollapseToStart
The selection's current contents are deleted before performing the insertion, and the TextSelection is left empty at the beginning of the newly inserted text.
vsInsertFlagsContainNewText
The selection's current contents are replaced with the inserted text, and the TextSelection is left containing the new text.
vsInsertFlagsInsertAtStart
The inserted text is placed at the beginning of the TextSelection, and the resulting TextSelection contains both the new and previous text.
vsInsertFlagsInsertAtEnd
The inserted text is placed at the end of the TextSelection, and the resulting TextSelection contains both the new and previous text.

Figure 6 AddCommand

  // AddCommand
// Add one of the commands, dealing with the errors that might result.
//
// szName            -name of the command being added
// szButtonText       text displayed on menu or button
// szToolTip        - text displayed in tool tip
// szKey            - default key assignment, or empty string if none
// szMenuToAddTo    - default menu to place on, or empty if none
//
private void AddCommand (String szName, String szButtonText,
                         String szToolTip, String szKey, 
                         String szMenuToAddTo)
{
    Command cmd = null;
    Object [] GuidArray = {};

    // The IDE identifies commands by their full name, which include
    // the add-in name
    //
    String szNameFull = "TextUtil.Connect." + szName;

    try 
    {
        cmd = applicationObject.Commands.Item (szNameFull, -1);
    }
    catch(System.ArgumentException /* e */)
    {
        // Thrown if command doesn't already exist.
    }

    if (null != cmd)
    {
        // While in development I delete the command each time in order
        // to ensure any changes I make in development are picked up.
        // Doing this will probably also remove any key bindings. You
        // will probably want to remove this prior to distributing.
        cmd.Delete();
        cmd = null;
    }

    if (null == cmd)
    {
        // Add new command to the IDE
        //
        cmd = applicationObject.Commands.AddNamedCommand (addInInstance
            , szName
            , szButtonText
            , szToolTip
            , false, 1, ref GuidArray, 0);
    }

    if ("" != szKey)
    {
        // a default keybinding specified
        //
        object [] bindings;
        bindings = (object [])cmd.Bindings;
        if (0 >= bindings.Length)
        {
            // there is no preexisting key binding, so add the default
            bindings = new object[1];
            bindings[0] = (object)szKey;
            cmd.Bindings = (object)bindings;
        }
    }

    if ("" != szMenuToAddTo)
    {
        // caller specified that this be added to a menu (aka, 
        // commandbar).
        //
        CommandBar commandBar = (CommandBar)
             applicationObject.CommandBars[szMenuToAddTo];
        cmd.AddControl(commandBar, commandBar.Controls.Count);
    }
}

Figure 7 JustifySimple

  // JustifySimple
// Justify a block of text between margins
// Just justify from current point to next blank line.
//
bool JustifySimple ()
{
    int          lineStart;    // line containing start of selection
    int          lineEnd;      // line containing end of selection
    EditPoint    pointCur;     // beginning of text to be justified
    EditPoint    pointDelete;  // end of text to be justified
    string []    rgszLines;    // array of lines contained in original text
    string       szLineTemp;   // temporary string while rebuilding text
    string       szText;       // text to be justified, as a single line

    // Locate the blank line that delimits the end of a "paragraph"
    //
    pointCur = ((TextSelection)
    applicationObject.ActiveDocument.Selection).AnchorPoint.CreateEditPoint();
    lineEnd = lineStart = pointCur.Line;
    do 
    {
        lineEnd++;
    }
    while (("" != pointCur.GetLines(lineEnd,lineEnd+1).Trim())
        && !pointCur.AtEndOfDocument);

    // Grab copy of text to be justified. Split into an array of
    // lines while I'm at it to facilitate EOL removal and space
    // trimming.
    //
    rgszLines = pointCur.GetLines(lineStart,lineEnd).Split('\r

    // Remove text from buffer.
    //
    pointCur.MoveToLineAndOffset(lineStart,1);
    pointDelete = pointCur.CreateEditPoint();
    pointDelete.MoveToLineAndOffset(lineEnd,1);
    pointCur.Delete (pointDelete);

    // Now concatenate all lines into one, with the line breaks
    // removed, spaces trimmed, and a single space between what
    // used to be the individual lines.
    //
    szText = "";
    for (int i = 0; i < rgszLines.Length; i++)
    {
        szLineTemp = TrimEOL(rgszLines[i]).Trim();
        if ("" != szLineTemp)
            szText += " " + szLineTemp;
    }
    szText = szText.Trim();
  
    // I now have one string that contains all the text to be
    // formatted out. I try to grab iWidth chunks from its
    // beginning and output each, until there is nothing left.
    //
    while ("" != szText)
    {
        if (iWidth < szText.Length)
        {
            int iSpace;
            
            // String is longer than width, so I need to parse off
            // a segment. I hope for word breaks (spaces), but if none,
            // I'll chop indiscriminately.
            //
            szLineTemp = szText.Substring (0, iWidth);
            iSpace = szLineTemp.LastIndexOf (' 
            if (0 <= iSpace)
                szLineTemp = szText.Substring (0, iSpace);
            szText = szText.Substring (szLineTemp.Length).Trim();
        }
        else
        {
            // String is shorter than width, so I just copy the remaining
            // string.
            //
            szLineTemp = szText;
            szText = "";
        }

        // output the modified line.
        //
        szLineTemp = szLineTemp.Trim() + "\r\n";
        pointCur.Insert (szLineTemp);
    }
    return true;
}

// TrimEOL
// remove all EOL characters (CR & LF) from a string.
//
private String TrimEOL(String sz)
{
    return sz.Replace ("\n", "").Replace ("\r", "");
}

Figure 8 Specifying Margins

  int            iColStart = 1;   // default starting column
int            iColEnd = 73;    // default ending column

if ((txt.Mode == EnvDTE.vsSelectionMode.vsSelectionModeBox) &&
    (txt.BottomPoint.VirtualDisplayColumn != 
    txt.TopPoint.VirtualDisplayColumn))
{
    // Selection is a box of some sort. That means I take the
    // left and right limits of the box as the margins.
    //
    iColStart = txt.TopPoint.VirtualDisplayColumn;
    iColEnd = txt.BottomPoint.VirtualDisplayColumn;
}

Figure 9 Justify

  // Justify
// Justify a block of text between margins
// Justify text multiline selection, if it exists,
// otherwise to blank line. Use existing selection width to
// determine width of justification. Treat as single, undoable
// action. Handles preserving line-prepended strings and reading
// defaults from the registry
//
bool Justify (string szPrepend)
{
    bool         fUndoWasOpen;       // undo context state on entry
    int          iColStart = 1;      // default starting column
    int          iColEnd = iWidth+1; // default ending column
    int          iWidthWrap;         // width of text to justify 
                                     // (End-Start)
    int          lineStart;
    int          lineEnd;
    EditPoint    pointCur;           // beginning of text to be justified
    EditPoint    pointDelete;        // end of text to be justified
    string []    rgszLines;          // array of lines contained in 
                                     // original text
    string        szLineTemp;        // temporary string while rebuilding 
                                     // text
    string        szText;            // text to be justified, as a single 
                                     // line
    TextSelection txt;               // the initial text selection

    fUndoWasOpen = applicationObject.UndoContext.IsOpen;
    if (!fUndoWasOpen)
        applicationObject.UndoContext.Open("TextUtil.Justify", false);

    try 
    {
        txt = (TextSelection)applicationObject.ActiveDocument.Selection;
        pointCur = txt.TopPoint.CreateEditPoint();
        lineStart = pointCur.Line;

        if (txt.BottomPoint.Line == pointCur.Line)
        {
            // Selection is on a single line, or nonexistent.
            // Locate the blank line that delimits the end of a paragraph
            //
            lineEnd = lineStart;
            do 
            {
                lineEnd++;
            }
            while (("" != pointCur.GetLines(lineEnd,lineEnd+1).Trim())
                && !pointCur.AtEndOfDocument);
        }
        else
        {
            lineEnd = txt.BottomPoint.Line+1;
        }

        if ((txt.Mode == EnvDTE.vsSelectionMode.vsSelectionModeBox) &&
            (txt.BottomPoint.VirtualDisplayColumn !=
                txt.TopPoint.VirtualDisplayColumn))
        {
            // selection is a box of some sort. That means I take the
            // left and right limits of the box as the margins
            //
            iColStart = txt.TopPoint.VirtualDisplayColumn;
            iColEnd = txt.BottomPoint.VirtualDisplayColumn;
        }

        // Grab copy of text to be justified. I split into an array of
        // lines while I'm at it to facilitate EOL removal and space
        // trimming.
        //
        rgszLines = pointCur.GetLines(lineStart,lineEnd).Split('\r

        // Remove text from buffer.
        //
        pointCur.MoveToLineAndOffset(lineStart,1);
        pointDelete = pointCur.CreateEditPoint();
        pointDelete.MoveToLineAndOffset(lineEnd,1);
        pointCur.Delete (pointDelete);

        // If the first line begins with our prepend string,
        // then check all the lines for that string, and remove
        //
        if ("" != szPrepend)
            if (!rgszLines[0].StartsWith (szPrepend))
                szPrepend = "";

        // Now concatenate all lines into one, with the line breaks
        // removed, spaces trimmed, and a single space between what
        // were the lines.
        //
        szText = "";
        for (int i = 0; i < rgszLines.Length; i++)
        {
            szLineTemp = TrimEOL(rgszLines[i]).Trim();
            if (0 < szPrepend.Length)
                if (szLineTemp.StartsWith (szPrepend))
                    szLineTemp = szLineTemp.Substring
                        (szPrepend.Length);
            if ("" != szLineTemp)
                szText += " " + szLineTemp;
        }
        szText = szText.Trim();

        // I now have one string that contains all the text to be
        // formatted out. I try to grab iWidthWrap chunks from its
        // beginning and output each, until there is nothing left.
        //
        iWidthWrap = iColEnd - iColStart - szPrepend.Length;
        while ("" != szText)
        {
            if (iWidthWrap < szText.Length)
            {
                int iSpace;
            
                // String is longer than width, so I need to parse off
                // a segment. I hope for word breaks (spaces), but if none,
                // I'll chop indiscriminately.
                //
                szLineTemp = szText.Substring (0, iWidthWrap);
                iSpace = szLineTemp.LastIndexOf (' 
                if (0 <= iSpace)
                    szLineTemp = szText.Substring (0, iSpace);
                szText = szText.Substring (szLineTemp.Length).Trim();
            }
            else
            {
                // String is shorter than width, so I just copy the 
                // remaining string.
                //
                szLineTemp = szText;
                szText = "";
            }

            // output the modified line.
            //
            szLineTemp = szPrepend + szLineTemp.Trim() + "\r\n";
            szLineTemp = szLineTemp.PadLeft (iColStart - 1 +
                szLineTemp.Length,' 
            pointCur.Insert (szLineTemp);
        }
    }
    finally 
    {
        if (!fUndoWasOpen)
            applicationObject.UndoContext.Close();
    }           
    return true;
}

Figure 11 OnAfterCreated and OnOK

  // Called each time the options page is about to be
// displayed. Load up current values and pre-fill
// edit boxes.
//
public void OnAfterCreated(
    DTE DTEObject
    ){
    int iWidth;
    RegistryKey key;

    key = Registry.LocalMachine.OpenSubKey
("Software\\Microsoft\\VisualStudio\\7.0\\Addins\\TextUtil.Connect\\Options");

    iWidth = (int)key.GetValue("iwidth", 72);
    txtRightColumn.Text = iWidth.ToString();
    txtCodePrefix.Text = (string)key.GetValue ("codeprefix", "// ");
    txtMailPrefix.Text = (string)key.GetValue ("mailprefix", "| ");
    }
public void OnOK(){
    RegistryKey key;
    key = Registry.LocalMachine.OpenSubKey
("Software\\Microsoft\\VisualStudio\\7.0\\Addins\\TextUtil.Connect\\Options", true);
    key.SetValue ("iwidth", System.Convert.ToInt32(txtRightColumn.Text));
    key.SetValue ("codeprefix", txtCodePrefix.Text);
    key.SetValue ("mailprefix", txtMailPrefix.Text);
}

Figure 12 Installing VSToolsOp

Figure 12 Installing VSToolsOp