Click here to Skip to main content
15,881,281 members
Articles / Programming Languages / C#

Automatic variable replacement in Visual Studio code pane

Rate me:
Please Sign up or sign in to vote.
4.85/5 (7 votes)
26 Nov 2006CPOL4 min read 44.9K   400   28   4
This article describes how to create an add-in to automatically replace variables as code is changed or added in Visual Studio.

Note: The demo files need to be placed in the "C:\Documents and Settings\user\My Documents\Visual Studio 2005\Addins" directory.

Image 1

Introduction

This article describes how to add variable replacements into your snippet generated code, or even a simple copy and paste function.

The first thing we need to do is trap the code changed event from the IDE, we need to have a mechanism that allows us to look up a value to replace. Now, we need to replace the inserted code. This will also allow us to use the functionality as an inline variable replacement, like Word uses auto-correct.

Secondly, we need this mechanism to work in conjunction with the snippet tool. This requires us to wrap our variables in a different way. The snippet uses the dollar ($) to pre and post sign the variables, so that they look like this: $username$. We will use a percent (%) like this %username%, to allow us to add static and variable values into our snippet design.

Thirdly, we want any changes made in our XML look up file to be reflected in the IDE immediately, this requires us to use a "FileSystemWatcher". If the file changes, the contents will automatically be reloaded into the snippet watcher.

Background

Have you ever wanted to add variables in your snippet code, variables that could be replaced at the time the code is being added to your IDE code pane. I wanted to utilise the built-in power of VS2005 snippets with the power of having a reusable template of lookup values that the snippet could look at before inserting the code. If you have a look at the vast examples of snippet code, you will more than likely see an example of how to create a "header.snippet"; see below. The snippet below is an example I put together to illustrate the point.

The fundamental problem with snippets is that you have no way of linking variables to a look up list of values like date (date format), time, or user name, so you have to hard code these values into the snippet or make the default not editable. This basically adds the value without putting that edit box over the value. This project rectifies this problem and opens up the snippet library to endless possibilities.

Example of snippet code

An example of a snippet code to generate a header section for your code block:

XML
<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  
       xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
           <Title>Header</Title>
           <Shortcut>Header</Shortcut>
           <Description>Code snippet Header statement</Description>
           <Author>Grant Simms</Author>
           <SnippetTypes>
            <SnippetType>Expansion</SnippetType>
           </SnippetTypes>
        </Header>
        <Snippet>
          <Declarations>
            <Literal default="true" Editable="true">
                        <ID>classname</ID>
              <Default>ClassName</Default>
                      </Literal>
          </Declarations>
<Code Language="csharp"> 
<![CDATA[#region Header 
//===================================================================
// Author:    %username%
// Date:        %date% time %timestamp%
// Description:     $classname$ , Description:
//
// 
// %copyright%
//===================================================================
#endregion $end$ ]]>
      </Code>
    </Snippet>
  </CodeSnippet>
</CodeSnippets>

The only problem with this is that you now have to enter data into the fields, i.e., the date, user name, and possibly some other information that would be impossible to have as a variable in the snippet.

The example above is what you are presented with after the snippet has completed, Fig 1.0. By tabbing, you move from block to block; however, if you could have a process that eliminates this tedious process by automatically replacing predefined variables with the corresponding data, see Fig 1.1, the process would be much simpler / quicker.

Images

Fig. 1.0

Image 2

Fig. 1.1

Image 3

Key stroke actions

The power of this process allows for a wide range of uses, from simple copy and paste actions that will convert the variables rapped in a percentage (%) to be looked up and replaced. If you want to type in a variable, i.e., %username% and press Enter, this will be converted to the value you placed in the XML document, like your name.

C#
private void Form1_Load(object sender, EventArgs e)
{
    %username%
}
private void Form1_Load(object sender, EventArgs e)
{
    Grant Simms
}

The XML document is loaded at IDE start so that the values are always available, this helps with speed. I have also added a FileSystemWatcher to reload the XML file if it changes at any time, so that the new or updated variables are immediately available. I have made use of Regular Expression algorithms to find and replace the values for speed.

Points of interest

The code to trap the IDE code changed event is as follows. This must be placed in the OnConnection call. This is also where the variables will need to be loaded into the StringDictionary for quick access later.

C#
_textEditorEvents = (TextEditorEvents)
   ((Events)_applicationObject.Events).get_TextEditorEvents(null);
_textEditorEvents.LineChanged += new 
   _dispTextEditorEvents_LineChangedEventHandler(
   _textEditorEvents_LineChanged);

The call to the _textEditorEvents_LineChanged event is as follows:

C#
void _textEditorEvents_LineChanged(TextPoint StartPoint, 
                       TextPoint EndPoint, int Hint)
{

    //IF you want to write a status on the IDE Status bar.
    // _applicationObject.StatusBar.Text = "Line Changing..."; 
    EditPoint ep = EndPoint.CreateEditPoint();
    EditPoint sp = StartPoint.CreateEditPoint();

    sp.CharLeft(1);
    string txt = sp.GetText(ep);

    try
    {
        MatchCollection matches = Regex.Matches(txt, "%.*%");
        if (matches.Count > 0)
        {
            foreach (Match match in matches)
            {
                sp.Delete(ep);
                string newtext = txt;
                IEnumerator myEnum = _stringDictionary.GetEnumerator();
                foreach (DictionaryEntry de in _stringDictionary)
                {
                    switch (de.Key.ToString())
                    {
                        case "%date%":
                            newtext = newtext.Replace(de.Key.ToString(), 
                              DateTime.Now.ToString(de.Value.ToString()));
                            break;
                        case "%datetimestamp%":
                            goto case "%date%";
                        case "%timestamp%":
                            goto case "%date%";
                        default:
                            newtext = newtext.Replace(de.Key.ToString(), 
                                      de.Value.ToString());
                            break;
                    }
                }
                sp.Insert(newtext);
                return;
            }
        }
    }
    catch
    {
        //Not going to trap anything...
    }
}

Setting up a FileSystemWatcher

If you want to monitor a directory for changes in a file, you need to point the FileSystemWatcher to the file and enable the EnableRaisingEvents, and then trap the events and do what you need to to do.

C#
#region fileSystemWatcher
fileSystemWatcher.BeginInit();
fileSystemWatcher.EnableRaisingEvents = true;
fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
fileSystemWatcher.Filter = filename;
fileSystemWatcher.IncludeSubdirectories = false;
fileSystemWatcher.Path = 
      System.IO.Path.GetDirectoryName(asmbly.Location);
fileSystemWatcher.Changed+=new 
      FileSystemEventHandler(fileSystemWatcher_Changed);
fileSystemWatcher.EndInit();
#endregion

This is where you trap the event:

C#
void fileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
{
  //This may fire the event more than once, 
  //this is due to the attributes of
  //the file also updating...
  LoadVariables();
}

The demo project is a release build that is fully functional, but I have included all the source code to replicate the entire add-in.

Have fun coding, and I hope this helps you as much as it has helped me in creating a vast reusable library of extensible snippets.

Notes & To-Do features

If you find this useful or have any suggestions that I can put in the next release to enhance it, please let me know.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect Contractor
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralAdaption to Visual Studio 2010 Pin
Peter Niederberghaus20-May-11 4:28
Peter Niederberghaus20-May-11 4:28 
Questionflaw? Pin
Piteros19-Aug-08 0:30
Piteros19-Aug-08 0:30 
GeneralGreat, just what I wanted Pin
snssswc1-Mar-07 10:36
snssswc1-Mar-07 10:36 
GeneralRe: Great, just what I wanted Pin
Grant Simms8-Mar-07 0:48
Grant Simms8-Mar-07 0:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.