65.9K
CodeProject 正在变化。 阅读更多。
Home

Visual Studio 代码窗体中的自动变量替换

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.85/5 (7投票s)

2006年7月12日

CPOL

4分钟阅读

viewsIcon

45926

downloadIcon

422

本文介绍如何创建一个插件,在 Visual Studio 中更改或添加代码时自动替换变量。

注意:演示文件需要放在 "C:\Documents and Settings\user\My Documents\Visual Studio 2005\Addins" 目录中。

引言

本文介绍如何将变量替换添加到代码片段生成的代码中,甚至可以实现简单的复制粘贴功能。

首先,我们需要捕获 IDE 的代码更改事件,需要有一个机制让我们查找要替换的值。现在,我们需要替换插入的代码。这还将允许我们将此功能用作内联变量替换,就像 Word 使用自动更正一样。

其次,我们需要此机制与代码片段工具协同工作。这需要我们以不同的方式包装我们的变量。代码片段使用美元符号($)来前后缀变量,使其看起来像这样:$username$。我们将使用百分号(%),如下所示:%username%,以便在代码片段设计中添加静态和变量值。

第三,我们希望在 XML 查找文件中所做的任何更改都能立即反映在 IDE 中,这需要我们使用“FileSystemWatcher”。如果文件发生更改,其内容将自动重新加载到代码片段观察器中。

背景

您是否曾想在代码片段代码中添加变量,这些变量可以在将代码添加到 IDE 代码窗格时进行替换?我想利用 VS2005 代码片段的内置功能,并结合一个可重用的查找值模板,代码片段可以在插入代码之前进行查找。如果您查看大量的代码片段示例,您很可能会看到如何创建“header.snippet”的示例;请参见下文。下面的代码片段是我为说明这一点而组合的示例。

代码片段的根本问题是,您无法将变量链接到值查找列表,例如日期(日期格式)、时间或用户名,因此您必须将这些值硬编码到代码片段中,或者使默认值不可编辑。这基本上是添加值,而不必在值上方显示编辑框。此项目解决了这个问题,并为代码片段库打开了无限的可能性。

代码片段代码示例

用于生成代码块标题部分的示例代码片段

<?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>

这唯一的问题是,您现在必须输入数据到字段中,即日期、用户名以及其他可能无法作为代码片段中的变量的其他信息。

上面的示例是代码片段完成后您将看到的,图 1.0。通过制表符,您可以从一个块移动到另一个块;但是,如果您可以有一个流程,通过自动替换预定义的变量及其相应的数据来消除这个繁琐的过程,请参见图 1.1,那么这个过程将更简单/更快。

图像

图 1.0

图 1.1

按键操作

此流程的强大功能允许广泛的使用,从简单的复制粘贴操作,可以将用百分号(%)括起来的变量查找并替换。如果您想输入一个变量,例如 %username%,然后按 Enter,它将被转换为您在 XML 文档中放置的值,例如您的名字。

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

XML 文档在 IDE 启动时加载,以便值始终可用,这有助于提高速度。我还添加了一个 FileSystemWatcher,以便在文件发生更改时重新加载 XML 文件,从而使新变量或更新的变量立即可用。我利用了正则表达式算法来快速查找和替换值。

关注点

捕获 IDE 代码更改事件的代码如下。这必须放在 OnConnection 调用中。这也是需要将变量加载到 StringDictionary 中以便稍后快速访问的地方。

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

调用 _textEditorEvents_LineChanged 事件的代码如下

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...
    }
}

设置 FileSystemWatcher

如果您想监视目录中文件的更改,则需要将 FileSystemWatcher 指向该文件并启用 EnableRaisingEvents,然后捕获事件并执行您需要执行的操作。

#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

这是您捕获事件的地方

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();
}

演示项目是完全可运行的发布版本,但我包含了所有源代码来复制整个插件。

祝您编码愉快,希望这对您创建大量可重用的、可扩展的代码片段库有所帮助。

注意事项与待办功能

如果您觉得这有用,或者有任何建议可以在下一版本中改进它,请告诉我。

© . All rights reserved.